--- /dev/null
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitmaps"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "bstr"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+dependencies = [
+ "lazy_static",
+ "memchr",
+ "regex-automata",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "bytesize"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70"
+
+[[package]]
+name = "cargo"
+version = "0.63.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d092a7c3e3aaa66469b2233b58c0bf330419dad9c423165f2b9cf1c57dc9f2e"
+dependencies = [
+ "anyhow",
+ "atty",
+ "bytesize",
+ "cargo-platform",
+ "cargo-util",
+ "clap",
+ "crates-io",
+ "crossbeam-utils",
+ "curl",
+ "curl-sys",
+ "env_logger",
+ "filetime",
+ "flate2",
+ "fwdansi",
+ "git2",
+ "git2-curl",
+ "glob",
+ "hex 0.4.3",
+ "home",
+ "humantime",
+ "ignore",
+ "im-rc",
+ "indexmap",
+ "itertools",
+ "jobserver",
+ "lazy_static",
+ "lazycell",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "memchr",
+ "num_cpus",
+ "opener",
+ "os_info",
+ "pathdiff",
+ "percent-encoding",
+ "rustc-workspace-hack",
+ "rustfix",
+ "semver",
+ "serde",
+ "serde_ignored",
+ "serde_json",
+ "shell-escape",
+ "strip-ansi-escapes",
+ "tar",
+ "tempfile",
+ "termcolor",
+ "toml_edit",
+ "unicode-width",
+ "unicode-xid",
+ "url",
+ "walkdir",
+ "winapi",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-util"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b75f6bfca7b85d6e8c6a42405e9b4ecadd2e63f75f94aabfb524378b57a557a4"
+dependencies = [
+ "anyhow",
+ "core-foundation",
+ "crypto-hash",
+ "filetime",
+ "hex 0.4.3",
+ "jobserver",
+ "libc",
+ "log",
+ "miow",
+ "same-file",
+ "shell-escape",
+ "tempfile",
+ "walkdir",
+ "winapi",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+dependencies = [
+ "jobserver",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-integer",
+ "num-traits",
+ "time",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "commoncrypto"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007"
+dependencies = [
+ "commoncrypto-sys",
+]
+
+[[package]]
+name = "commoncrypto-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "crates-io"
+version = "0.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4a87459133b2e708195eaab34be55039bc30e0d120658bd40794bb00b6328d"
+dependencies = [
+ "anyhow",
+ "curl",
+ "percent-encoding",
+ "serde",
+ "serde_json",
+ "url",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-hash"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca"
+dependencies = [
+ "commoncrypto",
+ "hex 0.3.2",
+ "openssl",
+ "winapi",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.59+curl-7.86.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407"
+dependencies = [
+ "cc",
+ "libc",
+ "libnghttp2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "winapi",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "debcargo"
+version = "2.6.0"
+dependencies = [
+ "ansi_term",
+ "anyhow",
+ "cargo",
+ "chrono",
+ "clap",
+ "env_logger",
+ "filetime",
+ "flate2",
+ "git2",
+ "glob",
+ "itertools",
+ "log",
+ "regex",
+ "semver",
+ "serde",
+ "serde_derive",
+ "tar",
+ "tempfile",
+ "textwrap",
+ "toml",
+ "walkdir",
+]
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "env_logger"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
+dependencies = [
+ "crc32fast",
+ "libz-sys",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fwdansi"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208"
+dependencies = [
+ "memchr",
+ "termcolor",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "git2"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c"
+dependencies = [
+ "bitflags",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "openssl-probe",
+ "openssl-sys",
+ "url",
+]
+
+[[package]]
+name = "git2-curl"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee51709364c341fbb6fe2a385a290fb9196753bdde2fc45447d27cd31b11b13"
+dependencies = [
+ "curl",
+ "git2",
+ "log",
+ "url",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
+[[package]]
+name = "globset"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "home"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "ignore"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
+dependencies = [
+ "crossbeam-utils",
+ "globset",
+ "lazy_static",
+ "log",
+ "memchr",
+ "regex",
+ "same-file",
+ "thread_local",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "im-rc"
+version = "15.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
+dependencies = [
+ "bitmaps",
+ "rand_core",
+ "rand_xoshiro",
+ "sized-chunks",
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "jobserver"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "libgit2-sys"
+version = "0.13.4+1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1"
+dependencies = [
+ "cc",
+ "libc",
+ "libssh2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "libnghttp2-sys"
+version = "0.1.7+1.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "libssh2-sys"
+version = "0.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "miow"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "opener"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
+dependencies = [
+ "bstr",
+ "winapi",
+]
+
+[[package]]
+name = "openssl"
+version = "0.10.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "os_info"
+version = "3.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4750134fb6a5d49afc80777394ad5d95b04bc12068c6abb92fae8f43817270f"
+dependencies = [
+ "log",
+ "serde",
+ "winapi",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc-workspace-hack"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb"
+
+[[package]]
+name = "rustfix"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
+dependencies = [
+ "anyhow",
+ "log",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+dependencies = [
+ "lazy_static",
+ "windows-sys 0.36.1",
+]
+
+[[package]]
+name = "scratch"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+
+[[package]]
+name = "semver"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_ignored"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b3da7eedd967647a866f67829d1c79d184d7c4521126e9cc2c46a9585c6d21"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "shell-escape"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
+
+[[package]]
+name = "sized-chunks"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
+dependencies = [
+ "bitmaps",
+ "typenum",
+]
+
+[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strip-ansi-escapes"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
+dependencies = [
+ "vte",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tar"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f"
+dependencies = [
+ "combine",
+ "indexmap",
+ "itertools",
+ "kstring",
+ "serde",
+]
+
+[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
+dependencies = [
+ "hashbrown",
+ "regex",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vte"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
+dependencies = [
+ "arrayvec",
+ "utf8parse",
+ "vte_generate_state_changes",
+]
+
+[[package]]
+name = "vte_generate_state_changes"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
+[[package]]
+name = "xattr"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
+dependencies = [
+ "libc",
+]
--- /dev/null
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "debcargo"
+version = "2.6.0"
+authors = [
+ "Josh Triplett <josh@joshtriplett.org>",
+ "Ximin Luo <infinity0@debian.org>",
+ "Vasudev Kamath <vasudev@copyninja.info>",
+]
+description = "Create a Debian package from a Cargo crate."
+readme = "README.md"
+license = "MIT/Apache-2.0"
+repository = "https://salsa.debian.org/rust-team/debcargo"
+resolver = "2"
+
+[dependencies.ansi_term]
+version = "0.12"
+
+[dependencies.anyhow]
+version = "1.0"
+
+[dependencies.cargo]
+version = "0.63"
+
+[dependencies.chrono]
+version = "0.4"
+
+[dependencies.clap]
+version = "3"
+features = [
+ "cargo",
+ "derive",
+]
+
+[dependencies.env_logger]
+version = "0.9"
+
+[dependencies.filetime]
+version = "0.2"
+
+[dependencies.flate2]
+version = "1"
+
+[dependencies.git2]
+version = "0.14"
+
+[dependencies.glob]
+version = "0.3"
+
+[dependencies.itertools]
+version = "0.10"
+
+[dependencies.log]
+version = "0.4"
+
+[dependencies.regex]
+version = "1.0"
+
+[dependencies.semver]
+version = "1"
+
+[dependencies.serde]
+version = "1"
+
+[dependencies.serde_derive]
+version = "1"
+
+[dependencies.tar]
+version = "0.4"
+
+[dependencies.tempfile]
+version = "3"
+
+[dependencies.textwrap]
+version = "0.16"
+
+[dependencies.toml]
+version = "0.5"
+
+[dependencies.walkdir]
+version = "2"
--- /dev/null
+[package]
+name = "debcargo"
+version = "2.6.0"
+authors = [
+ "Josh Triplett <josh@joshtriplett.org>",
+ "Ximin Luo <infinity0@debian.org>",
+ "Vasudev Kamath <vasudev@copyninja.info>"]
+license = "MIT/Apache-2.0"
+repository = "https://salsa.debian.org/rust-team/debcargo"
+description = "Create a Debian package from a Cargo crate."
+edition = "2021"
+
+[dependencies]
+ansi_term = "0.12"
+anyhow = "1.0"
+cargo = "0.63"
+clap = { version = "3", features = ["cargo", "derive"] }
+chrono = "0.4"
+env_logger = "0.9"
+filetime = "0.2"
+flate2 = "1"
+git2 = "0.14"
+glob = "0.3"
+itertools = "0.10"
+log = "0.4"
+regex = "1.0"
+semver = "1"
+serde = "1"
+serde_derive = "1"
+tar = "0.4"
+textwrap = "0.16"
+tempfile = "3"
+toml = "0.5"
+walkdir = "2"
--- /dev/null
+This file contains information about developing debcargo itself.
+
+
+## Dependencies
+
+For testing:
+
+```shell
+# Install dependencies for building (see README.md), then:
+$ cargo install cargo-tree # use https://github.com/infinity0/cargo-tree
+$ apt-get install dh-cargo lintian
+```
+
+For development:
+
+```shell
+# As above, then:
+$ cargo install rustfmt cargo-graph cargo-outdated
+$ cargo graph | dot -T png > graph.png
+$ cargo outdated -R
+```
+
+
+## Testing
+
+Whenever you make a major change, you should run:
+
+ tests/sh/integrate.sh -kbr debcargo exa fd-find ripgrep
+
+in order to test it over a few hundred crates. Fix any build errors and
+important lintian errors that crop up.
+
+If you make a change that has wide-reaching implications, such as messing with
+the dependency logic, do a more thorough test:
+
+ tests/sh/integrate.sh -kbR debcargo exa fd-find ripgrep mdbook sccache
+
+This will run the test over around a thousand crates. -R runs it over all the
+transitive dependencies of all the binary packages, which is needed for entry
+into Debian Testing. This is wider than -r, which runs the test over all the
+transitive build-dependencies of the source package, which is needed for entry
+into Debian Unstable.
+
+### Details
+
+To test the `debcargo` produced packages, you can run the following script.
+
+```shell
+$ tests/sh/integrate.sh crate[s]
+```
+
+where you can provide a list of crate names or local directories containing
+crates, and the script will run debcargo to create a source package (`.dsc`)
+and run lintian over it. If you find any issues, please add to the bugs in
+TODO.md file.
+
+```shell
+$ tests/sh/integrate.sh -kb crate[s]
+```
+
+will additionally run [sbuild](https://wiki.debian.org/sbuild) on the source
+package to build binary Debian packages (`.deb`) and run lintian over that too.
+It will automatically pick up any extra .debs you already have in the output
+directory, if they are dependencies of what you're building. The `-k` flag
+tells the script not to wipe the directory before it does anything else.
+
+```shell
+$ tests/sh/integrate.sh -kbr crate[s]
+```
+
+will run the script recursively over the listed crate(s) and all the transitive
+build-dependencies of the generated source packages, in dependency order. This
+covers all the dependencies that are needed for entry into Debian Unstable, and
+typically covers a few hundred crates. You may want or need to edit or update
+some of the overrides in `tests/configs`, to prune old or buggy dependencies.
+
+```shell
+$ tests/sh/integrate.sh -kbR crate[s]
+```
+
+will run the script recursively over the listed crate(s) and all the transitive
+runtime-dependencies of the binary packages, in dependency order. This covers
+all the dependencies that are needed for entry into Debian Testing, and
+typically covers a few thousand crates. You may want or need to edit or update
+some of the overrides in `tests/configs`, to prune old or buggy dependencies.
+
+See `-h` for other options.
--- /dev/null
+Crates.io to Debian
+===========================
+
+`debcargo` creates Debian source package from the provided Rust crate. Create
+Debian source package from the downloaded crate which follows Rust teams crate
+packaging [policy](https://wiki.debian.org/Teams/RustPackaging/Policy).
+
+
+## Features ##
+
+ * Package specific versions of crates from crates.io.
+ * Easy to customize, using config files and overlay directories.
+ * Tries to auto-detect copyright information from metadata and actual
+ crate source, used to guess appropriate values for `debian/copyright`.
+ * Put `FIXME (hint)` strings where it can't detect full information, so user can
+ provide an override/overlay or manually fix it.
+ * Results in a lintian-clean Debian package in most cases.
+ * Packages can be cross-compiled by `sbuild` assuming the crate doesn't
+ include anything that breaks it (such as arch-specific build.rs scripts).
+
+
+## Dependencies
+
+For building:
+
+```shell
+$ apt-get build-dep cargo
+$ apt-get install libssl-dev libcurl4-gnutls-dev quilt
+$ cargo build debcargo
+```
+
+
+## Examples ##
+
+To download and unpack the latest `clap` crate and prepare the source package:
+
+```shell
+$ debcargo package clap
+```
+
+To download and unpack version `2.25.0` of `clap` crate and prepare the source package:
+
+```shell
+$ debcargo package clap =2.25.0
+```
+
+To provide additional packaging-specific config for downloading and packaging
+latest `clap` crate from crates.io:
+
+```shell
+$ debcargo package --config clap-2/debian/debcargo.toml clap
+```
+
+See `debcargo.toml.example` for a sample TOML file.
+
+
+### Long-term maintenance workflow
+
+See https://salsa.debian.org/rust-team/debcargo-conf/blob/master/README.rst
+for an example.
+
+
+## License ##
+
+Debcargo is licensed under `MIT/Apache-2.0`. It is written by `Josh Triplett`
+and improved by members of **Debian Rust Maintainers team**
--- /dev/null
+See https://salsa.debian.org/groups/rust-team/-/issues
+
+See HACKING.md for development and testing instructions.
+
+## Lower-priority tasks
+
+Minor issues
+
+- fs::rename cannot handle cross-device moves, e.g. if --directory is on a
+ different partition from . then debcargo fails
+
+- the ? syntax loses the stack, use Result.expect() to give context, or use
+ error-chain instead...
+
+- [ ] globset, ignore, termcolor:
+ When generating d/copyright, failed to clone repository
+ https://github.com/BurntSushi/ripgrep/tree/master/XXX: unexpected HTTP status code: 404; class=Net (12)
+
+Features for later
+
+- [ ] Integrate `apt-pkg-native` crate to check if the crate or its dependency
+ is already in archive and display information.
+- [ ] Display first level dependency with equivalent Debian names at the end
+ which are not yet packaged in Debian as a information to maintainer.
+- [ ] A `dependency` sub-command to print all the dependencies including first
+ level and recursive using `cargo` API.
+- [ ] Employ `licensecheck` tool to look for license and copyright information.
+ Currently we use regex to grep through sources.
--- /dev/null
+# Whether to generate a package for the binary crate. If omitted, defaults to
+# true unless semver_suffix (see below) is also true.
+#bin = true
+
+# Name for the binary crate. Defaults to "<default>" which means the crate
+# name, with no "rust-" prefix and with underscores replaced by hyphens.
+#bin_name = "<default>"
+
+# Add the semver to the package name, to allow co-installation with other
+# versions of the same crate. This should only be true for crates older than
+# the most up-to-date version in Debian, and only if they are needed as a
+# (direct or indirect) build dependency of another binary crate.
+#
+# If you set this to true, you should either omit "bin" or set it to false,
+# unless you are sure the old and new packages are co-installable.
+#semver_suffix = false
+
+# Overlay directory to copy on top of the generated one, given relative to the
+# directory that contains this config file. If any files conflict with the ones
+# generated by debcargo, the latter are moved to <file>.debcargo.hint instead.
+#
+# For the special case of debian/changelog, generated entries will be prepended
+# to the top of the existing d/changelog (from the overlay), rather than stored
+# in debian/changelog.debcargo.hint. A further exception: if the distribution
+# of the top entry in the existing d/changelog is
+# UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO then that whole entry will be
+# *replaced* with the generated entry, not prepended.
+#
+# Normally, any "hints" generated are written back to the overlay directory,
+# overwriting any hints that may have previously been stored there. For the
+# special case of debian/changelog, that is written-back as-is. Therefore, it
+# is strongly recommended that you keep the overlay directory tracked in
+# version control. To disable this behaviour, give --no-overlay-write-back on
+# the debcargo command line.
+#
+#overlay = "."
+
+# Local directory where crate can be found, instead of crates.io. Resolved
+# relative to the directory that contains this config file. Note: this is
+# currently experimental and only works for crates whose dependencies are all
+# published on crates.io. For example, not rustup. This limitation will
+# hopefully be fixed in the future.
+#
+#crate_src_path = "../.."
+
+# Paths from the crate tarball, to exclude from the orig tarball.
+# See https://docs.rs/glob/latest/glob/struct.Pattern.html for syntax
+#excludes = ["libgit2/**"]
+
+# Paths from the crate tarball, that have been manually reviewed to adhere to
+# Debian policy. debcargo has a crude method for detecting files that might not
+# fit within policy, and will give a fatal error if any are detected. In the
+# exceptional cases where the method gives a false-positive, add them here.
+#whitelist = ["libgit2/**"]
+
+# Whether to allow prerelease deps, by rewriting these to the released version.
+# This should only be enabled for certain crates if really necessary, and first
+# you should check that they can actually build when this is enabled.
+#allow_prerelease_deps = false
+
+# This is the stem of the short description for each binary package. By default
+# `debcargo` will try to auto-extract a description from `Cargo.toml` but
+# sometimes this may lead to a meaningless, weird short description.
+#
+# The full short description of each binary package is constructed using this
+# string plus an auto-generated suffix. The full string can be overridden by
+# the [packages.KEY].summary config key, see below.
+#summary = "PLACEHOLDER"
+
+# This is the stem of the long description for each binary package. By default
+# this is empty.
+#
+# The full long description of each binary package is constructed using this
+# string plus an auto-generated suffix. The full string can be overridden by
+# the [packages.KEY].description config key, see below.
+#description = """
+#PLACEHOLDER
+#"""
+
+# Maintainer
+#
+# Defaults to pkg-rust-team, should only be overriden with care. If overridden,
+# vcs_* in [source] should also be overridden, otherwise they point the Rust
+# Team's salsa project.
+#
+#maintainer= "PLACEHOLDER"
+
+# Uploaders. This affects the Uploaders: field in debian/control as well as the
+# additional maintainers listed in debian/copyright. The naming is historical;
+# in Debian today for team-maintained packages, this is generally taken to mean
+# "the main individuals" responsible for the package - anyone on the team is
+# "morally allowed" to perform the upload as long as they specify "Team upload"
+# in debian/changelog; this is done automatically by debcargo.
+uploaders = [ "foo bar <foo@debian.org>" ]
+
+# This is a temporary work-around in order to address situations where certain
+# Debian infrastructure people claim (without supplying concrete evidence) that
+# rust crate metadata is "too large". This flag addresses this, effectively by
+# forcing all crate features together into a single feature. This increases the
+# dependency footprint of the generated packages and therefore should not be
+# enabled unless absolutely necessary. It can also cause cyclic dependencies in
+# some cases, and in these cases it simply cannot be enabled, as packages in
+# the cycle become uninstallable. Most crates should not need this, and you
+# should not enable this just because "somebody told you so".
+#collapse_features = false
+
+[source]
+
+# Debian Standards-Version to use. By default debcargo uses latest policy version.
+#policy = "4.0.0"
+
+# Override or provide missing homepage for crate
+#homepage = "https://clap.rs"
+
+# Override the VCS entries.
+# By default this points to a relevant subdirectory underneath the main
+# repository for debcargo config files, owned by the Debian Rust Maintainers:
+# https://salsa.debian.org/rust-team/debcargo-conf/
+# Please only override this if your package is truly special, e.g. it combines
+# lots of languages and/or there is not a crate at the top-level directory. In
+# most cases you should prefer packaging as part of the Debian Rust Team, see
+# https://wiki.debian.org/Teams/RustPackaging/Policy
+#
+#vcs_git = "https://salsa.debian.org/special_package/rust-special-0.1.git"
+#vcs_browser = "https://salsa.debian.org/special_package/rust-special-0.1"
+
+# Section override for the source package. Unless overridden here, library
+# crates get "rust" and non-library crates get a "FIXME".
+#section = "rust"
+
+# Extra Build-Depends on top of those generated by debcargo.
+# If you defined a custom d/rules that does extra stuff on top of dh-cargo,
+# then you may need to use this.
+#
+# OTOH, if your crate needs external development headers to build, these should
+# probably instead go in the [packages.lib] depends key rather than this key.
+# debcargo will then automatically add those into the package Build-Depends if
+# needed by dh-cargo; you don't have to add them here as well.
+#build_depends = ["PLACEHOLDER", "PLACEHOLDER"]
+
+# Build-Depends to subtract from those generated by debcargo. This should be
+# used when our default generated Build-Depends results in a cycle. For
+# example, this might happen if:
+#
+# - crate A's default-feature depends on crate B's no-default-features (which has no dependencies)
+# - crate B's default-feature depends on crate A's no-default-features (which has no dependencies)
+#
+# In these cases, you'll need to (for example) add "librustA+B-dev <!nocheck>"
+# to build_depends_excludes in A's debcargo.toml to break the cycle, and also
+# add override_dh_auto_test to A's d/rules to avoid selecting the B feature
+# when running the test build. Depending on the exact situation, it should be
+# sufficient to do this override only for one of the packages in the cycle.
+#
+# Note that binary package Depends must be left alone in order to correctly
+# express the dependencies; these ought not to have cycles in anyway, even in
+# a case like the above example.
+#
+# This field should *not* be used to exclude arch-specific dependencies. We
+# want to include them to support cross-compiling, and they should cause no
+# problems since they are just source code. If our test "cargo build" fails for
+# one of those dependencies, it should be handled in that package by disabling
+# the failing test on the architectures that they are expected to fail on.
+#build_depends_excludes = ["PLACEHOLDER", "PLACEHOLDER"]
+
+# Binary package overrides.
+#
+# Different values for KEY selects different binary packages:
+# lib - the package for the library crate
+# "lib+FEATURE" - the metapackage for feature FEATURE
+# bin - the package for the binary crate
+#
+[packages.KEY]
+
+# Section override for the binary package. Use this if your crate is both a
+# library and a binary crate; in this case, omit source.section which will
+# default to "rust" and override this value for your binary package.
+#section = "PLACEHOLDER"
+
+# Short description for the package. If omitted, debcargo autogenerates this
+# using the top-level "summary" key plus a suffix describing the feature.
+#summary = "PLACEHOLDER"
+
+# Long description for the package. If omitted, debcargo autogenerates this
+# using the top-level "description" key plus a suffix describing the feature.
+#description = """
+#PLACEHOLDER
+#"""
+
+# Additional Depends on top of the ones generated by debcargo. This should be
+# used to pull in system libraries for crates that need them to build. You'll
+# want the -dev versions of the library packages, since our crate packages are
+# development packages and not runtime packages.
+#depends = ["PLACEHOLDER", "PLACEHOLDER"]
+
+# We generate an autopkgtest (post-install test) for every feature, and also
+# run `cargo test` for the default feature set during build-time if there are
+# no additional dev-dependencies.
+#
+# However sometimes this may not work, e.g. if the crate is part of a larger
+# workspace and its tests require files from the workspace directory. Or if
+# the crate author is simply negligent and didn't ensure the test passes for
+# all features. In these cases, you can use this setting to mark the test as
+# "flaky" to ignore failures. Special cases for packages.KEY:
+#
+# packages."lib+@" - disables the test for the --all-features autopkgtest
+#
+# The effect is transitive to its reverse-dependencies, so e.g. if you specify
+# this for feature A, and feature B depends on feature A, then feature B also
+# implicitly has this set. To unset it on feature B (and its transitive rdeps),
+# explicit set this to false for feature B as well.
+#
+# So for example if the test breaks for the bare library (when running with
+# --no-default-features) but works when the std feature is enabled, set this to
+# true for [packages.lib] and false for [packages."lib+std"].
+#
+# Note: debcargo will error if you set these in an inconsistent way. For
+# example if feature A depends on {B, C}, B says true and C says false, of
+# course we cannot determine what A should be. You can suppress the error by
+# explicitly giving A a value, either true or false.
+#
+#test_is_broken = false
+
+# Some tests depend on extra system tools or libraries, which need to be given
+# to autopkgtest. Like test_is_broken, the effect is transitive to its rdeps.
+#test_depends = []
+#
+# Note: we do not (currently) support unsetting of this in indirect rdeps. This
+# would result in similar conflict issues as documented for test_is_broken. It
+# is possible to work around it similarly, by requiring manual resolution - but
+# cancelling dependencies is harder to implement than true/false so we avoid it
+# for now. Please file an issue if you have a real use-case for it.
+
+# More additional fields. This is mostly useful for binary packages that might
+# relate to other external programs, e.g. debcargo Recommends cargo.
+#recommends = ["PLACEHOLDER", "PLACEHOLDER"]
+#suggests = ["PLACEHOLDER", "PLACEHOLDER"]
+#provides = ["PLACEHOLDER", "PLACEHOLDER"]
+
+# Extra lines to include in the stanza, freeform. Use this to include things
+# that debcargo doesn't handle, such as Breaks, Conflicts, Replaces.
+#extra_lines = ["PLACEHOLDER", "PLACEHOLDER"]
--- /dev/null
+use ansi_term::Colour::Red;
+use clap::{crate_version, AppSettings, Parser};
+
+use debcargo::crates::CrateInfo;
+use debcargo::debian::DebInfo;
+use debcargo::errors::Result;
+use debcargo::package::*;
+use debcargo::{
+ build_order::{build_order, BuildOrderArgs},
+ crates::invalidate_crates_io_cache,
+};
+
+#[derive(Debug, Clone, Parser)]
+#[clap(name = "debcargo", about = "Package Rust crates for Debian.")]
+enum Opt {
+ /// Update the user's default crates.io index, outside of a workspace.
+ Update,
+ /// Print the Debian package name for a crate.
+ DebSrcName {
+ /// Name of the crate to package.
+ crate_name: String,
+ /// Version of the crate to package; may contain dependency operators.
+ /// If empty string, resolves to the latest version. If given here,
+ /// i.e. not omitted then print the package name as if the config
+ /// option semver_suffix was set to true.
+ version: Option<String>,
+ },
+ /// Extract only a crate, without any other transformations.
+ Extract {
+ #[clap(flatten)]
+ init: PackageInitArgs,
+ #[clap(flatten)]
+ extract: PackageExtractArgs,
+ },
+ /// Package a Rust crate for Debian.
+ Package {
+ #[clap(flatten)]
+ init: PackageInitArgs,
+ #[clap(flatten)]
+ extract: PackageExtractArgs,
+ #[clap(flatten)]
+ finish: PackageExecuteArgs,
+ },
+ /// Print the transitive dependencies of a package in topological order.
+ BuildOrder {
+ #[clap(flatten)]
+ args: BuildOrderArgs,
+ },
+}
+
+#[test]
+fn verify_app() {
+ use clap::IntoApp;
+ Opt::into_app().debug_assert()
+}
+
+fn real_main() -> Result<()> {
+ let m = Opt::clap()
+ .version(crate_version!())
+ .global_setting(AppSettings::ColoredHelp)
+ .get_matches();
+ use Opt::*;
+ match Opt::from_clap(&m) {
+ Update => invalidate_crates_io_cache(),
+ DebSrcName {
+ crate_name,
+ version,
+ } => {
+ let crate_info = CrateInfo::new_with_update(&crate_name, version.as_deref(), false)?;
+ let deb_info = DebInfo::new(&crate_info, crate_version!(), version.is_some());
+ println!("{}", deb_info.package_name());
+ Ok(())
+ }
+ Extract { init, extract } => {
+ log::info!("preparing crate info");
+ let mut process = PackageProcess::init(init)?;
+ log::info!("extracting crate");
+ process.extract(extract)?;
+ Ok(())
+ }
+ Package {
+ init,
+ extract,
+ finish,
+ } => {
+ log::info!("preparing crate info");
+ let mut process = PackageProcess::init(init)?;
+ log::info!("extracting crate");
+ process.extract(extract)?;
+ log::info!("applying overlay and patches");
+ process.apply_overrides()?;
+ log::info!("preparing orig tarball");
+ process.prepare_orig_tarball()?;
+ log::info!("preparing debian folder");
+ process.prepare_debian_folder(finish)?;
+ process.post_package_checks()
+ }
+ BuildOrder { args } => {
+ let build_order = build_order(args)?;
+ for v in &build_order {
+ println!("{}", v);
+ }
+ Ok(())
+ }
+ }
+}
+
+fn main() {
+ env_logger::init();
+ if let Err(e) = real_main() {
+ eprintln!("{}", Red.bold().paint(format!("debcargo failed: {:?}", e)));
+ std::process::exit(1);
+ }
+}
--- /dev/null
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
+use std::fmt;
+use std::path::{Path, PathBuf};
+
+use anyhow::Context;
+use cargo::core::{Dependency, PackageId};
+use clap::{Parser, ValueEnum};
+
+use crate::config::Config;
+use crate::crates::{crate_name_ver_to_dep, show_dep, transitive_deps, CrateDepInfo, CrateInfo};
+use crate::debian::control::base_deb_name;
+use crate::errors::Result;
+use crate::package::{PackageExtractArgs, PackageProcess};
+use crate::util;
+
+#[derive(Debug, Clone, Copy, ValueEnum)]
+#[clap(rename_all = "verbatim")]
+pub enum ResolveType {
+ SourceForDebianUnstable,
+ BinaryAllForDebianTesting,
+}
+
+#[derive(Debug, Clone, Parser)]
+pub struct BuildOrderArgs {
+ /// Name of the crate to package.
+ crate_name: String,
+ /// Version of the crate to package; may contain dependency operators.
+ version: Option<String>,
+ /// Directory for configs. The config subdirectory for a given crate is
+ /// looked up by their crate name and version, from more specific to less
+ /// specific, e.g. <crate>-1.2.3, then <crate>-1.2, then <crate>-1 and
+ /// finally <crate>. The config file is read from the debian/debcargo.toml
+ /// subpath of the looked-up subdirectory.
+ #[clap(long)]
+ config_dir: Option<PathBuf>,
+ /// Resolution type
+ #[clap(value_enum, long, default_value = "SourceForDebianUnstable")]
+ resolve_type: ResolveType,
+ /// Emulate resolution as if every package were built with --collapse-features.
+ #[clap(long)]
+ emulate_collapse_features: bool,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct PackageIdFeat(PackageId, &'static str);
+
+impl fmt::Display for PackageIdFeat {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}@{}/{}", self.0.name(), self.0.version(), self.1)
+ }
+}
+
+// First result: if somebody build-depends on us, what do they first need to build?
+// Second result: what other packages need to go into Debian Testing before us?
+fn get_build_deps(
+ crate_details: &(CrateInfo, CrateDepInfo, Config),
+ package: &PackageIdFeat,
+ resolve_type: ResolveType,
+ emulate_collapse_features: bool,
+) -> Result<(Vec<Dependency>, Vec<Dependency>)> {
+ let (_, crate_dep_info, config) = crate_details;
+ let all_deps = crate_dep_info
+ .iter()
+ .map(|(_, v)| v.1.iter())
+ .flatten()
+ .cloned()
+ .collect::<HashSet<_>>();
+ let feature_deps: HashSet<Dependency> =
+ HashSet::from_iter(transitive_deps(crate_dep_info, package.1).1);
+ let additional_deps = if emulate_collapse_features || config.collapse_features {
+ all_deps.clone()
+ } else {
+ // TODO: if build_depends_features is an override, use that instead of "default"
+ // TODO: also deprecate build_depends_excludes
+ HashSet::from_iter(transitive_deps(crate_dep_info, "default").1)
+ };
+ let hard_deps = feature_deps
+ .union(&additional_deps)
+ .cloned()
+ .collect::<Vec<_>>();
+ use ResolveType::*;
+ match resolve_type {
+ SourceForDebianUnstable => Ok((hard_deps, vec![])),
+ BinaryAllForDebianTesting => {
+ let mut soft_deps = all_deps;
+ for h in hard_deps.iter() {
+ soft_deps.remove(h);
+ }
+ Ok((hard_deps, soft_deps.into_iter().collect::<Vec<_>>()))
+ }
+ }
+}
+
+fn dep_features(dep: &Dependency) -> Vec<&'static str> {
+ let mut feats = dep
+ .features()
+ .iter()
+ .map(|x| x.as_str())
+ .collect::<Vec<_>>();
+ if dep.uses_default_features() {
+ feats.push("default")
+ }
+ feats.push(""); // bare-bones library with no features
+ feats
+}
+
+fn find_config(config_dir: &Path, id: PackageId) -> Result<(Option<PathBuf>, Config)> {
+ let name = base_deb_name(&id.name());
+ let version = id.version();
+ let candidates = [
+ format!(
+ "{}-{}.{}.{}",
+ name, version.major, version.minor, version.patch
+ ),
+ format!("{}-{}.{}", name, version.major, version.minor),
+ format!("{}-{}", name, version.major),
+ name,
+ ];
+ for c in candidates {
+ let path = config_dir.join(c).join("debian").join("debcargo.toml");
+ if path.is_file() {
+ let config = Config::parse(&path).context("failed to parse debcargo.toml")?;
+ log::debug!("using config for {}: {:?}", id, path);
+ return Ok((Some(path), config));
+ }
+ }
+ Ok((None, Config::default()))
+}
+
+fn resolve_info(
+ infos: &mut BTreeMap<PackageId, (CrateInfo, CrateDepInfo, Config)>,
+ cache: &mut HashMap<Dependency, PackageId>,
+ config_dir: Option<&Path>,
+ dependency: &Dependency,
+ update: bool,
+) -> Result<PackageId> {
+ if let Some(id) = cache.get(dependency) {
+ return Ok(*id);
+ }
+
+ // resolve dependency
+ let info = CrateInfo::new_from_dependency(dependency, update)?;
+ let id = info.package_id();
+ cache.insert(dependency.clone(), id);
+
+ // insert info if it's not already there
+ if let std::collections::btree_map::Entry::Vacant(e) = infos.entry(id) {
+ let id = *e.key();
+ let default_config = Config::default();
+ let (config_path, config) = match config_dir {
+ None => (None, default_config),
+ Some(config_dir) => {
+ let (config_path, config) = find_config(config_dir, id)?;
+ match config_path {
+ None => (None, default_config),
+ Some(_) => (config_path, config),
+ }
+ }
+ };
+ let (info, config) = match config_path {
+ None => (info, config),
+ Some(_) => {
+ let mut process = PackageProcess::new(info, config_path, config)?;
+ let tempdir = tempfile::Builder::new()
+ .prefix("debcargo")
+ .tempdir_in(".")?;
+ process.extract(PackageExtractArgs {
+ directory: Some(tempdir.path().to_path_buf()),
+ })?;
+ process.apply_overrides()?;
+ (process.crate_info, process.config)
+ }
+ };
+ let dep_info = info.all_dependencies_and_features();
+ e.insert((info, dep_info, config));
+ };
+ Ok(id)
+}
+
+pub fn build_order(args: BuildOrderArgs) -> Result<Vec<PackageId>> {
+ let crate_name = &args.crate_name;
+ let version = args.version.as_deref();
+ let config_dir = args.config_dir.as_deref();
+
+ let mut infos = BTreeMap::new();
+ let mut cache = HashMap::new();
+ let seed_dep = crate_name_ver_to_dep(crate_name, version)?;
+ let seed_id = resolve_info(&mut infos, &mut cache, config_dir, &seed_dep, true)?;
+
+ let mut next = |idf: &PackageIdFeat| -> Result<(Vec<PackageIdFeat>, Vec<PackageIdFeat>)> {
+ let (hard, soft) = get_build_deps(
+ infos
+ .get(&idf.0)
+ .expect("build_order next called without crate info"),
+ idf,
+ args.resolve_type,
+ args.emulate_collapse_features,
+ )?;
+ log::trace!("{} hard-dep: {}", idf, util::show_vec_with(&hard, show_dep));
+ if !soft.is_empty() {
+ log::trace!("{} soft-dep: {}", idf, util::show_vec_with(&soft, show_dep));
+ }
+ // note: we might resolve the same crate-version several times;
+ // this is expected, since different dependencies (with different
+ // version ranges) might resolve into the same crate-version
+ let mut hard_p = Vec::new();
+ for dep in hard {
+ let id = resolve_info(&mut infos, &mut cache, config_dir, &dep, false)?;
+ for f in dep_features(&dep) {
+ hard_p.push(PackageIdFeat(id, f));
+ }
+ }
+ let mut soft_p = Vec::new();
+ for dep in soft {
+ let id = resolve_info(&mut infos, &mut cache, config_dir, &dep, false)?;
+ for f in dep_features(&dep) {
+ soft_p.push(PackageIdFeat(id, f));
+ }
+ }
+ log::trace!("{} hard-dep resolve: {}", idf, util::show_vec(&hard_p));
+ if !soft_p.is_empty() {
+ log::trace!("{} soft-dep resolve: {}", idf, util::show_vec(&soft_p));
+ }
+ Ok((hard_p, soft_p))
+ };
+ let mut i = 0;
+ let mut log = |remaining: &VecDeque<_>, graph: &BTreeMap<_, _>| {
+ i += 1;
+ if i % 16 == 0 {
+ debcargo_info!(
+ "debcargo build-order: resolving dependencies: done: {}, todo: {}",
+ graph.len(),
+ remaining.len()
+ );
+ }
+ Ok(())
+ };
+
+ let succ_with_features =
+ util::graph_from_succ([PackageIdFeat(seed_id, "")], &mut next, &mut log)?;
+ log::trace!("succ_with_features: {:#?}", succ_with_features);
+
+ let succ = util::succ_proj(&succ_with_features, |x| x.0);
+ let pred = util::succ_to_pred(&succ);
+ let roots = succ
+ .iter()
+ .filter_map(|(k, v)| if v.is_empty() { Some(*k) } else { None })
+ .collect::<BTreeSet<_>>();
+ // swap pred/succ for call to topo_sort since we want reverse topo order
+ let build_order = match util::topo_sort(roots, pred.clone(), succ.clone()) {
+ Ok(r) => r,
+ Err(remain) => {
+ log::error!(
+ "topo_sort got cyclic graph: {:#?}",
+ remain
+ .into_iter()
+ .map(|(k, vv)| (
+ k.to_string(),
+ vv.into_iter()
+ .map(|v| v.to_string())
+ .collect::<BTreeSet<_>>()
+ ))
+ .collect::<BTreeMap<_, _>>()
+ );
+ debcargo_bail!(
+ "topo_sort got cyclic graph; you'll need to patch the crate(s) to break the cycle."
+ )
+ }
+ };
+
+ // sanity check
+ for p in build_order.iter() {
+ if infos.remove(p).is_none() {
+ log::error!("extra package in build-order not in infos: {}", p);
+ }
+ }
+ for (p, _) in infos {
+ log::error!(
+ "leftover infos not used in build-order: {}, succ: {}, pred: {}",
+ p,
+ util::show_vec(succ.get(&p).into_iter().flatten()),
+ util::show_vec(pred.get(&p).into_iter().flatten()),
+ );
+ }
+
+ Ok(build_order)
+}
--- /dev/null
+use serde::Deserialize;
+use toml;
+
+use crate::errors::*;
+
+use std::collections::HashMap;
+use std::fs::File;
+use std::io::Read;
+use std::path::{Path, PathBuf};
+
+pub const RUST_MAINT: &str =
+ "Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>";
+
+#[derive(Deserialize, Debug, Clone)]
+#[serde(default)]
+pub struct Config {
+ pub bin: Option<bool>,
+ pub bin_name: String,
+ pub semver_suffix: bool,
+ pub overlay: Option<PathBuf>,
+ pub excludes: Option<Vec<String>>,
+ pub whitelist: Option<Vec<String>>,
+ pub allow_prerelease_deps: bool,
+ pub crate_src_path: Option<PathBuf>,
+ pub summary: Option<String>,
+ pub description: Option<String>,
+ pub maintainer: String,
+ pub uploaders: Option<Vec<String>>,
+ pub collapse_features: bool,
+ pub requires_root: Option<String>,
+
+ pub source: Option<SourceOverride>,
+ pub packages: Option<HashMap<String, PackageOverride>>,
+}
+
+#[derive(Deserialize, Debug, Clone, Default)]
+pub struct SourceOverride {
+ section: Option<String>,
+ policy: Option<String>,
+ homepage: Option<String>,
+ vcs_git: Option<String>,
+ vcs_browser: Option<String>,
+ build_depends: Option<Vec<String>>,
+ build_depends_excludes: Option<Vec<String>>,
+}
+
+#[derive(Deserialize, Debug, Clone, Default)]
+pub struct PackageOverride {
+ section: Option<String>,
+ summary: Option<String>,
+ description: Option<String>,
+ depends: Option<Vec<String>>,
+ recommends: Option<Vec<String>>,
+ suggests: Option<Vec<String>>,
+ provides: Option<Vec<String>>,
+ extra_lines: Option<Vec<String>>,
+ test_is_broken: Option<bool>,
+ test_depends: Option<Vec<String>>,
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Config {
+ bin: None,
+ bin_name: "<default>".to_string(),
+ semver_suffix: false,
+ overlay: None,
+ excludes: None,
+ whitelist: None,
+ allow_prerelease_deps: false,
+ crate_src_path: None,
+ summary: None,
+ description: None,
+ maintainer: RUST_MAINT.to_string(),
+ uploaders: None,
+ collapse_features: false,
+ source: None,
+ packages: None,
+ requires_root: None,
+ }
+ }
+}
+
+impl Config {
+ pub fn parse(src: &Path) -> Result<Config> {
+ let mut config_file = File::open(src)?;
+ let mut content = String::new();
+ config_file.read_to_string(&mut content)?;
+
+ Ok(toml::from_str(&content)?)
+ }
+
+ pub fn build_bin_package(&self) -> bool {
+ match self.bin {
+ None => !self.semver_suffix,
+ Some(b) => b,
+ }
+ }
+
+ pub fn overlay_dir(&self, config_path: Option<&Path>) -> Option<PathBuf> {
+ Some(config_path?.parent()?.join(self.overlay.as_ref()?))
+ }
+
+ pub fn crate_src_path(&self, config_path: Option<&Path>) -> Option<PathBuf> {
+ Some(config_path?.parent()?.join(self.crate_src_path.as_ref()?))
+ }
+
+ pub fn orig_tar_excludes(&self) -> Option<&Vec<String>> {
+ self.excludes.as_ref()
+ }
+
+ pub fn orig_tar_whitelist(&self) -> Option<&Vec<String>> {
+ self.whitelist.as_ref()
+ }
+
+ pub fn maintainer(&self) -> &str {
+ self.maintainer.as_str()
+ }
+
+ pub fn uploaders(&self) -> Option<&Vec<String>> {
+ self.uploaders.as_ref()
+ }
+
+ pub fn requires_root(&self) -> Option<&String> {
+ self.requires_root.as_ref()
+ }
+
+ // Source shortcuts
+
+ pub fn section(&self) -> Option<&str> {
+ Some(self.source.as_ref()?.section.as_ref()?)
+ }
+
+ pub fn policy_version(&self) -> Option<&str> {
+ Some(self.source.as_ref()?.policy.as_ref()?)
+ }
+
+ pub fn homepage(&self) -> Option<&str> {
+ Some(self.source.as_ref()?.homepage.as_ref()?)
+ }
+
+ pub fn vcs_git(&self) -> Option<&str> {
+ Some(self.source.as_ref()?.vcs_git.as_ref()?)
+ }
+
+ pub fn vcs_browser(&self) -> Option<&str> {
+ Some(self.source.as_ref()?.vcs_browser.as_ref()?)
+ }
+
+ pub fn build_depends(&self) -> Option<&Vec<String>> {
+ self.source.as_ref()?.build_depends.as_ref()
+ }
+
+ pub fn build_depends_excludes(&self) -> Option<&Vec<String>> {
+ self.source.as_ref()?.build_depends_excludes.as_ref()
+ }
+
+ // Packages shortcuts
+
+ fn with_package<'a, T, F: FnOnce(&'a PackageOverride) -> Option<T>>(
+ &'a self,
+ key: PackageKey,
+ f: F,
+ ) -> Option<T> {
+ self.packages
+ .as_ref()?
+ .get(&package_key_string(key))
+ .and_then(f)
+ }
+
+ pub fn package_section(&self, key: PackageKey) -> Option<&str> {
+ self.with_package(key, |pkg| pkg.section.as_deref())
+ }
+
+ pub fn package_summary(&self, key: PackageKey) -> Option<&str> {
+ self.with_package(key, |pkg| pkg.summary.as_deref())
+ }
+
+ pub fn package_description(&self, key: PackageKey) -> Option<&str> {
+ self.with_package(key, |pkg| pkg.description.as_deref())
+ }
+
+ pub fn package_depends(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.depends.as_ref())
+ }
+
+ pub fn package_recommends(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.recommends.as_ref())
+ }
+
+ pub fn package_suggests(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.suggests.as_ref())
+ }
+
+ pub fn package_provides(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.provides.as_ref())
+ }
+
+ pub fn package_extra_lines(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.extra_lines.as_ref())
+ }
+
+ pub fn package_test_is_broken(&self, key: PackageKey) -> Option<bool> {
+ self.with_package(key, |pkg| pkg.test_is_broken)
+ }
+
+ pub fn package_test_depends(&self, key: PackageKey) -> Option<&Vec<String>> {
+ self.with_package(key, |pkg| pkg.test_depends.as_ref())
+ }
+}
+
+pub fn package_field_for_feature<'a>(
+ get_field: &'a dyn Fn(PackageKey) -> Option<&'a Vec<String>>,
+ feature: PackageKey,
+ f_provides: &[&str],
+) -> Vec<String> {
+ Some(feature)
+ .into_iter()
+ .chain(f_provides.iter().map(|s| PackageKey::feature(s)))
+ .map(move |f| get_field(f).into_iter().flatten())
+ .flatten()
+ .map(|s| s.to_string())
+ .collect()
+}
+
+#[derive(Clone, Copy)]
+pub enum PackageKey<'a> {
+ Bin,
+ BareLib,
+ FeatureLib(&'a str),
+}
+
+impl<'a> PackageKey<'a> {
+ pub fn feature(f: &'a str) -> PackageKey<'a> {
+ use self::PackageKey::*;
+ if f.is_empty() {
+ BareLib
+ } else {
+ FeatureLib(f)
+ }
+ }
+}
+
+fn package_key_string(key: PackageKey) -> String {
+ use self::PackageKey::*;
+ match key {
+ Bin => "bin".to_string(),
+ BareLib => "lib".to_string(),
+ FeatureLib(feature) => format!("lib+{}", feature),
+ }
+}
+
+pub fn testing_ignore_debpolv() -> bool {
+ std::env::var("DEBCARGO_TESTING_IGNORE_DEBIAN_POLICY_VIOLATION") == Ok("1".to_string())
+}
+
+pub fn testing_ruzt() -> bool {
+ std::env::var("DEBCARGO_TESTING_RUZT") == Ok("1".to_string())
+}
--- /dev/null
+use anyhow::{format_err, Error};
+use cargo::{
+ core::manifest::ManifestMetadata,
+ core::registry::PackageRegistry,
+ core::source::MaybePackage,
+ core::{
+ resolver::features::CliFeatures, Dependency, EitherManifest, FeatureValue, Manifest,
+ Package, PackageId, Registry, Source, SourceId, Summary, Target, TargetKind, Workspace,
+ },
+ ops,
+ ops::{PackageOpts, Packages},
+ sources::RegistrySource,
+ util::{interning::InternedString, toml::read_manifest, FileLock},
+ Config,
+};
+use filetime::{set_file_times, FileTime};
+use flate2::read::GzDecoder;
+use glob::Pattern;
+use regex::Regex;
+use semver::Version;
+use tar::Archive;
+use tempfile;
+
+use std;
+use std::collections::{BTreeMap, HashSet};
+use std::fs;
+use std::hash::{Hash, Hasher};
+use std::io::{self, Read, Write};
+use std::path::Path;
+
+use crate::config::testing_ignore_debpolv;
+use crate::errors::*;
+
+pub struct CrateInfo {
+ // only used for to_registry_toml in extract_crate. DO NOT USE ELSEWHERE
+ package: Package,
+ // allows overriding package.manifest() e.g. via patches
+ manifest: Manifest,
+ crate_file: FileLock,
+ config: Config,
+ source_id: SourceId,
+ excludes: Vec<Pattern>,
+ includes: Vec<Pattern>,
+}
+
+pub type CrateDepInfo = BTreeMap<
+ &'static str, // name of feature / optional dependency,
+ // or "" for the base package w/ no default features, guaranteed to be in the map
+ (
+ Vec<&'static str>, // dependencies: other features (of the current package)
+ Vec<Dependency>,
+ ),
+>;
+
+fn hash<H: Hash>(hashable: &H) -> u64 {
+ #![allow(deprecated)]
+ let mut hasher = std::hash::SipHasher::new();
+ hashable.hash(&mut hasher);
+ hasher.finish()
+}
+
+fn fetch_candidates(registry: &mut PackageRegistry, dep: &Dependency) -> Result<Vec<Summary>> {
+ let mut summaries = match registry.query_vec(dep, false) {
+ std::task::Poll::Ready(res) => res?,
+ std::task::Poll::Pending => {
+ registry.block_until_ready()?;
+ return fetch_candidates(registry, dep);
+ }
+ };
+ summaries.sort_by(|a, b| b.package_id().partial_cmp(&a.package_id()).unwrap());
+ Ok(summaries)
+}
+
+pub fn invalidate_crates_io_cache() -> Result<()> {
+ let config = Config::default()?;
+ let _lock = config.acquire_package_cache_lock()?;
+ let source_id = SourceId::crates_io(&config)?;
+ let yanked_whitelist = HashSet::new();
+ let mut r = RegistrySource::remote(source_id, &yanked_whitelist, &config)?;
+ r.invalidate_cache();
+ Ok(())
+}
+
+pub fn crate_name_ver_to_dep(crate_name: &str, version: Option<&str>) -> Result<Dependency> {
+ // note: this forces a network call
+ let config = Config::default()?;
+ let source_id = SourceId::crates_io(&config)?;
+ let version = version.and_then(|v| {
+ if v.is_empty() {
+ None
+ } else if v.starts_with(|c: char| c.is_digit(10)) {
+ Some(["=", v].concat())
+ } else {
+ Some(v.to_string())
+ }
+ });
+ Dependency::parse(crate_name, version.as_deref(), source_id)
+}
+
+pub fn show_dep(dep: &Dependency) -> String {
+ format!("{} {}", dep.package_name(), dep.version_req())
+}
+
+impl CrateInfo {
+ pub fn new(crate_name: &str, version: Option<&str>) -> Result<CrateInfo> {
+ CrateInfo::new_with_update(crate_name, version, true)
+ }
+
+ pub fn new_with_local_crate(
+ crate_name: &str,
+ version: Option<&str>,
+ crate_path: &Path,
+ ) -> Result<CrateInfo> {
+ let config = Config::default()?;
+ let crate_path = crate_path.canonicalize()?;
+ let source_id = SourceId::for_path(&crate_path)?;
+
+ let (package, crate_file) = {
+ let yanked_whitelist = HashSet::new();
+
+ let mut source = source_id.load(&config, &yanked_whitelist)?;
+
+ let package_id = match version {
+ None | Some("") => {
+ let dep = Dependency::parse(crate_name, None, source_id)?;
+ let mut package_id: Option<PackageId> = None;
+ loop {
+ match source.query(&dep, &mut |p| package_id = Some(p.package_id())) {
+ std::task::Poll::Ready(res) => {
+ res?;
+ break;
+ }
+ std::task::Poll::Pending => {
+ source.block_until_ready()?;
+ }
+ }
+ }
+ package_id.unwrap()
+ }
+ Some(version) => PackageId::new(crate_name, version, source_id)?,
+ };
+
+ let maybe_package = source.download(package_id)?;
+ let package = match maybe_package {
+ MaybePackage::Ready(p) => Ok(p),
+ _ => Err(format_err!(
+ "Failed to 'download' local crate {} from {}",
+ crate_name,
+ crate_path.display()
+ )),
+ }?;
+
+ let crate_file = {
+ let workspace = Workspace::ephemeral(package.clone(), &config, None, true)?;
+
+ let opts = PackageOpts {
+ config: &config,
+ verify: false,
+ list: false,
+ check_metadata: true,
+ allow_dirty: true,
+ cli_features: CliFeatures::from_command_line(&[], true, false)?,
+ jobs: None,
+ targets: Vec::new(),
+ to_package: Packages::Default,
+ keep_going: false,
+ };
+
+ // as of cargo 0.41 this returns a FileLock with a temp path, instead of the one
+ // it got renamed to
+ if ops::package(&workspace, &opts)?.is_none() {
+ return Err(format_err!(
+ "Failed to assemble crate file for local crate {} at {}\n",
+ crate_name,
+ crate_path.display()
+ ));
+ }
+ let filename = format!("{}-{}.crate", crate_name, package_id.version().to_string());
+ workspace
+ .target_dir()
+ .join("package")
+ .open_rw(&filename, &config, "crate file")?
+ };
+
+ (package, crate_file)
+ };
+
+ let manifest = package.manifest().clone();
+
+ Ok(CrateInfo {
+ package,
+ manifest,
+ crate_file,
+ config,
+ source_id,
+ excludes: vec![],
+ includes: vec![],
+ })
+ }
+
+ pub fn new_with_update(
+ crate_name: &str,
+ version: Option<&str>,
+ update: bool,
+ ) -> Result<CrateInfo> {
+ let dep = crate_name_ver_to_dep(crate_name, version)?;
+ Self::new_from_dependency(&dep, update)
+ }
+
+ pub fn new_from_dependency(dependency: &Dependency, update: bool) -> Result<CrateInfo> {
+ let mut config = Config::default()?;
+ if !update {
+ // unfriendly API from cargo; we'll have to make do with it for
+ // now as there is no other alternative
+ config.configure(
+ 0,
+ false,
+ None,
+ config.frozen(),
+ config.locked(),
+ true, // offline
+ &config.target_dir()?.map(|x| x.into_path_unlocked()),
+ &[],
+ &[],
+ )?;
+ }
+
+ let source_id = dependency.source_id();
+ let registry_name = format!(
+ "{}-{:016x}",
+ source_id.url().host_str().unwrap_or(""),
+ hash(&source_id).swap_bytes()
+ );
+ let get_package_info = |config: &Config| -> Result<_> {
+ let lock = config.acquire_package_cache_lock()?;
+ let mut registry = PackageRegistry::new(config)?;
+ registry.lock_patches();
+ let summaries = fetch_candidates(&mut registry, dependency)?;
+ drop(lock);
+ let pkgids = summaries
+ .into_iter()
+ .map(|s| s.package_id())
+ .collect::<Vec<_>>();
+ let pkgid = pkgids.iter().max().ok_or_else(|| {
+ format_err!(
+ concat!(
+ "Couldn't find any crate matching {}\n",
+ "Try `debcargo update` to update the crates.io index."
+ ),
+ show_dep(dependency)
+ )
+ })?;
+ let pkgset = registry.get(pkgids.as_slice())?;
+ let package = pkgset.get_one(*pkgid)?;
+ let manifest = package.manifest();
+ for f in dependency.features() {
+ // apparently, if offline is set then cargo sometimes selects
+ // an offline-available version that doesn't satisfy the
+ // requested features. this is dumb. if it happens, then we
+ // retry with online allowed.
+ if !manifest.summary().features().contains_key(f) {
+ debcargo_bail!(
+ "resolve ({} {}) -> ({}) failed to pick up required feature ({})\n\
+ This can happen with very old or yanked crates. Try patching one of \
+ its dependants, to drop or update the offending dependency.",
+ dependency.package_name(),
+ dependency.version_req(),
+ pkgid,
+ f,
+ )
+ }
+ }
+ let filename = format!("{}-{}.crate", pkgid.name(), pkgid.version());
+ let crate_file = config
+ .registry_cache_path()
+ .join(®istry_name)
+ .open_ro(&filename, config, &filename)?;
+ Ok((package.clone(), manifest.clone(), crate_file))
+ };
+ // if update is false but the user never downloaded the crate then the
+ // first call will error; re-try with online in that case
+ let (package, manifest, crate_file) =
+ get_package_info(&config).or_else(|_| get_package_info(&Config::default()?))?;
+
+ Ok(CrateInfo {
+ package,
+ manifest,
+ crate_file,
+ config,
+ source_id,
+ excludes: vec![],
+ includes: vec![],
+ })
+ }
+
+ pub fn crate_name(&self) -> &'static str {
+ self.package_id().name().as_str()
+ }
+
+ pub fn version(&self) -> &Version {
+ self.package_id().version()
+ }
+
+ pub fn semver(&self) -> String {
+ match *self.package_id().version() {
+ Version {
+ major: 0, minor, ..
+ } => format!("0.{}", minor),
+ Version { major, .. } => format!("{}", major),
+ }
+ }
+
+ pub fn manifest(&self) -> &Manifest {
+ &self.manifest
+ }
+
+ pub fn replace_manifest(&mut self, path: &Path) -> Result<&Self> {
+ if let (EitherManifest::Real(v), _) = read_manifest(path, self.source_id, &self.config)? {
+ self.manifest = v;
+ }
+ Ok(self)
+ }
+
+ pub fn metadata(&self) -> &ManifestMetadata {
+ self.manifest.metadata()
+ }
+
+ pub fn manifest_path(&self) -> &Path {
+ self.package.manifest_path()
+ }
+
+ pub fn targets(&self) -> &[Target] {
+ self.manifest.targets()
+ }
+
+ pub fn is_lib(&self) -> bool {
+ let mut lib = false;
+ for target in self.manifest.targets() {
+ match *target.kind() {
+ TargetKind::Lib(_) => {
+ lib = true;
+ break;
+ }
+ _ => continue,
+ }
+ }
+ lib
+ }
+
+ pub fn get_binary_targets(&self) -> Vec<&str> {
+ let mut bins = Vec::new();
+ for target in self.manifest.targets() {
+ match *target.kind() {
+ TargetKind::Bin => {
+ bins.push(target.name());
+ }
+ _ => continue,
+ }
+ }
+ bins.sort_unstable();
+ bins
+ }
+
+ pub fn summary(&self) -> &Summary {
+ self.manifest.summary()
+ }
+
+ pub fn checksum(&self) -> Option<&str> {
+ self.manifest.summary().checksum()
+ }
+
+ pub fn package_id(&self) -> PackageId {
+ self.manifest.summary().package_id()
+ }
+
+ pub fn crate_file(&self) -> &FileLock {
+ &self.crate_file
+ }
+
+ pub fn dependencies(&self) -> &[Dependency] {
+ self.manifest.dependencies()
+ }
+
+ pub fn dev_dependencies(&self) -> Vec<Dependency> {
+ use cargo::core::dependency::DepKind;
+ let mut deps = vec![];
+ for dep in self.dependencies() {
+ if dep.kind() == DepKind::Development {
+ deps.push(dep.clone())
+ }
+ }
+ deps
+ }
+
+ /// Collect information about the dependency structure of features and
+ /// their external crate dependencies, in a simple output format.
+ pub fn all_dependencies_and_features(&self) -> CrateDepInfo {
+ use cargo::core::dependency::DepKind;
+
+ let mut deps_by_name: BTreeMap<&str, Vec<&Dependency>> = BTreeMap::new();
+ for dep in self.dependencies() {
+ // we treat build-dependencies also as dependencies in Debian
+ if dep.kind() != DepKind::Development {
+ let s = dep.name_in_toml().as_str();
+ deps_by_name.entry(s).or_default().push(dep);
+ }
+ }
+ let deps_by_name = deps_by_name;
+
+ let mut features_with_deps = BTreeMap::new();
+
+ // calculate dependencies of this crate's features
+ for (feature, deps) in self.manifest.summary().features() {
+ let mut feature_deps: Vec<&'static str> = vec![];
+ let mut other_deps: Vec<Dependency> = Vec::new();
+ for dep in deps {
+ use self::FeatureValue::*;
+ match dep {
+ // another feature is a dependency
+ Feature(dep_feature) => {
+ feature_deps.push(InternedString::new(dep_feature).as_str())
+ }
+ // another package is a dependency
+ Dep { dep_name } => {
+ // unwrap is ok, valid Cargo.toml files must have this
+ for &dep in deps_by_name.get(dep_name.as_str()).unwrap() {
+ other_deps.push(dep.clone());
+ }
+ }
+ // another package is a dependency
+ DepFeature {
+ dep_name,
+ dep_feature,
+ ..
+ } => match deps_by_name.get(dep_name.as_str()) {
+ // unwrap is ok, valid Cargo.toml files must have this
+ Some(dd) => {
+ for &dep in dd {
+ let mut dep = dep.clone();
+ let mut features: Vec<InternedString> =
+ vec![InternedString::new(dep_feature)];
+ features.extend(dep.features());
+ dep.set_features(features);
+ dep.set_default_features(false);
+ other_deps.push(dep);
+ }
+ }
+ None => {
+ let mut expected = false;
+ for dep in self.dependencies() {
+ if dep.kind() == DepKind::Development {
+ let s = dep.name_in_toml().as_str();
+ if s == dep_name.as_str() {
+ expected = true;
+ }
+ }
+ }
+ if expected {
+ debcargo_warn!(
+ "Ignoring \"{}\" feature \"{}\" as it depends on a \
+ dev-dependency \"{}\"",
+ self.package_id(),
+ feature,
+ dep_name
+ );
+ } else {
+ panic!(
+ "failed to account for dependency \"{}\" of \"{}\" feature \"{}\"",
+ dep_name, self.package_id(), feature
+ );
+ }
+ }
+ },
+ }
+ }
+ if feature_deps.is_empty() {
+ // everything depends on bare library
+ feature_deps.push("");
+ }
+ features_with_deps.insert(feature.as_str(), (feature_deps, other_deps));
+ }
+
+ // calculate required dependencies for implicit no-default-features
+ let mut deps_required: Vec<Dependency> = Vec::new();
+ for deps in deps_by_name.values() {
+ for &dep in deps {
+ if !dep.is_optional() {
+ deps_required.push(dep.clone())
+ }
+ }
+ }
+
+ // implicit no-default-features
+ features_with_deps.insert("", (vec![], deps_required));
+
+ // implicit default feature
+ if !features_with_deps.contains_key("default") {
+ features_with_deps.insert("default", (vec![""], vec![]));
+ }
+
+ features_with_deps
+ }
+
+ pub fn get_summary_description(&self) -> (Option<String>, Option<String>) {
+ let (summary, description) = if let Some(ref description) = self.metadata().description {
+ // Convention these days seems to be to do manual text
+ // wrapping in crate descriptions, boo. \n\n is a real line break.
+ let mut description = description
+ .replace("\n\n", "\r")
+ .replace("\n", " ")
+ .replace("\r", "\n")
+ .trim()
+ .to_string();
+ // Trim off common prefixes
+ let re = Regex::new(&format!(
+ r"^(?i)({}|This(\s+\w+)?)(\s*,|\s+is|\s+provides)\s+",
+ self.package_id().name()
+ ))
+ .unwrap();
+ description = re.replace(&description, "").to_string();
+ let re = Regex::new(r"^(?i)(a|an|the)\s+").unwrap();
+ description = re.replace(&description, "").to_string();
+ let re =
+ Regex::new(r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+")
+ .unwrap();
+ description = re.replace(&description, "").to_string();
+
+ // https://stackoverflow.com/questions/38406793/why-is-capitalizing-the-first-letter-of-a-string-so-convoluted-in-rust
+ description = {
+ let mut d = description.chars();
+ match d.next() {
+ None => String::new(),
+ Some(f) => f.to_uppercase().chain(d).collect::<String>(),
+ }
+ };
+
+ // Use the first sentence or first line, whichever comes first, as the summary.
+ let p1 = description.find('\n');
+ let p2 = description.find(". ");
+ match p1.into_iter().chain(p2.into_iter()).min() {
+ Some(p) => {
+ let s = description[..p].trim_end_matches('.').to_string();
+ let d = description[p + 1..].trim();
+ if d.is_empty() {
+ (Some(s), None)
+ } else {
+ (Some(s), Some(d.to_string()))
+ }
+ }
+ None => (Some(description.trim_end_matches('.').to_string()), None),
+ }
+ } else {
+ (None, None)
+ };
+
+ (summary, description)
+ }
+
+ /// To be called before extract_crate.
+ pub fn set_includes_excludes(
+ &mut self,
+ excludes: Option<&Vec<String>>,
+ includes: Option<&Vec<String>>,
+ ) {
+ self.excludes = excludes
+ .into_iter()
+ .flatten()
+ .map(|x| Pattern::new(&("*/".to_owned() + x)).unwrap())
+ .collect::<Vec<_>>();
+ self.includes = includes
+ .into_iter()
+ .flatten()
+ .map(|x| Pattern::new(&("*/".to_owned() + x)).unwrap())
+ .collect::<Vec<_>>();
+ }
+
+ pub fn filter_path(&self, path: &Path) -> ::std::result::Result<bool, String> {
+ if self.excludes.iter().any(|p| p.matches_path(path)) {
+ return Ok(true);
+ }
+ let suspicious = match path.extension() {
+ Some(ext) => ext == "c" || ext == "a",
+ _ => false,
+ };
+ if suspicious {
+ if self.includes.iter().any(|p| p.matches_path(path)) {
+ debcargo_info!("Suspicious file, on whitelist so ignored: {:?}", path);
+ Ok(false)
+ } else if testing_ignore_debpolv() {
+ debcargo_warn!("Suspicious file, ignoring as per override: {:?}", path);
+ Ok(false)
+ } else {
+ Err(format!(
+ "Suspicious file, should probably be excluded: {:?}",
+ path
+ ))
+ }
+ } else {
+ Ok(false)
+ }
+ }
+
+ pub fn extract_crate(&self, path: &Path) -> Result<bool> {
+ let mut archive = Archive::new(GzDecoder::new(self.crate_file.file()));
+ let tempdir = tempfile::Builder::new()
+ .prefix("debcargo")
+ .tempdir_in(".")?;
+ let mut source_modified = false;
+ let mut last_mtime = 0;
+ let mut err = vec![];
+
+ for entry in archive.entries()? {
+ let mut entry = entry?;
+ match self.filter_path(&(entry.path()?)) {
+ Err(e) => err.push(e),
+ Ok(r) => {
+ if r {
+ source_modified = true;
+ continue;
+ }
+ }
+ }
+
+ if !entry.unpack_in(tempdir.path())? {
+ debcargo_bail!("Crate contained path traversals via '..'");
+ }
+
+ if let Ok(mtime) = entry.header().mtime() {
+ if mtime > last_mtime {
+ last_mtime = mtime;
+ }
+ }
+ }
+ if !err.is_empty() {
+ for e in err {
+ debcargo_warn!("{}", e);
+ }
+ debcargo_bail!(
+ "Suspicious files detected, aborting. Ask on #debian-rust if you are stuck."
+ )
+ }
+
+ let entries = tempdir.path().read_dir()?.collect::<io::Result<Vec<_>>>()?;
+ if entries.len() != 1 || !entries[0].file_type()?.is_dir() {
+ let pkgid = self.package_id();
+ debcargo_bail!(
+ "{}-{}.crate did not unpack to a single top-level directory",
+ pkgid.name(),
+ pkgid.version()
+ );
+ }
+
+ if let Err(e) = fs::rename(entries[0].path(), &path) {
+ return Err(Error::from(e).context(format!(
+ concat!(
+ "Could not create source directory {0}\n",
+ "To regenerate, move or remove {0}"
+ ),
+ path.display()
+ )));
+ }
+
+ // Ensure that Cargo.toml is in standard form, e.g. does not contain
+ // path dependencies, so can be built standalone (see #4030).
+ let toml_path = path.join("Cargo.toml");
+ let ws = Workspace::new(&toml_path.canonicalize()?, &self.config)?;
+ let registry_toml = self.package.to_registry_toml(&ws)?;
+ let mut actual_toml = String::new();
+ fs::File::open(&toml_path)?.read_to_string(&mut actual_toml)?;
+
+ if !actual_toml.contains("AUTOMATICALLY GENERATED BY CARGO") {
+ // This logic should only fire for old crates, and that's what the
+ // if-conditional is supposed to check; modern versions of cargo
+ // already do this before uploading the crate and we shouldn't need
+ // to handle it specially.
+ let old_toml_path = path.join("Cargo.toml.orig");
+ fs::copy(&toml_path, &old_toml_path)?;
+ fs::OpenOptions::new()
+ .write(true)
+ .truncate(true)
+ .open(&toml_path)?
+ .write_all(registry_toml.as_bytes())?;
+ debcargo_info!(
+ "Rewrote {:?} to canonical form\nOld backed up as {:?}",
+ &toml_path,
+ &old_toml_path,
+ );
+ source_modified = true;
+ // avoid lintian errors about package-contains-ancient-file
+ // TODO: do we want to do this for unmodified tarballs? it would
+ // force us to modify them, but otherwise we get that ugly warning
+ let last_mtime = FileTime::from_unix_time(last_mtime as i64, 0);
+ set_file_times(toml_path, last_mtime, last_mtime)?;
+ }
+ Ok(source_modified)
+ }
+}
+
+/// Calculate all feature-dependencies and external-dependencies of a given
+/// feature, using the information previously generated by
+/// `all_dependencies_and_features`.
+pub fn transitive_deps<'a>(
+ features_with_deps: &'a CrateDepInfo,
+ feature: &str,
+) -> (Vec<&'a str>, Vec<Dependency>) {
+ let mut all_features = Vec::new();
+ let mut all_deps = Vec::new();
+ let &(ref ff, ref dd) = features_with_deps.get(feature).unwrap();
+ all_features.extend(ff.clone());
+ all_deps.extend(dd.clone());
+ for f in ff {
+ let (ff1, dd1) = transitive_deps(features_with_deps, f);
+ all_features.extend(ff1);
+ all_deps.extend(dd1);
+ }
+ (all_features, all_deps)
+}
--- /dev/null
+use anyhow;
+use chrono::{DateTime, FixedOffset, Local, TimeZone};
+use regex::Regex;
+
+use std::fmt;
+use std::str;
+
+pub const DEFAULT_DIST: &str = "UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO";
+pub const COMMENT_TEAM_UPLOAD: &str = " * Team upload.";
+
+pub struct ChangelogEntry {
+ pub source: String,
+ pub version: String,
+ pub distribution: String,
+ pub options: String,
+ pub maintainer: String,
+ pub date: DateTime<FixedOffset>,
+ pub items: Vec<String>,
+}
+
+pub fn local_now() -> DateTime<FixedOffset> {
+ let now = Local::now();
+ let offset = now
+ .timezone()
+ .offset_from_local_datetime(&now.naive_local())
+ .unwrap();
+ now.with_timezone(&offset)
+}
+
+impl fmt::Display for ChangelogEntry {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "{} ({}) {}; {}\n",
+ self.source, self.version, self.distribution, self.options
+ )?;
+
+ for entry in self.items.iter() {
+ writeln!(f, "{}", entry)?;
+ }
+
+ writeln!(f, "\n -- {} {}", self.maintainer, self.date.to_rfc2822())
+ }
+}
+
+fn line_is_blank(s: &str) -> bool {
+ s.chars().all(char::is_whitespace)
+}
+
+impl str::FromStr for ChangelogEntry {
+ type Err = anyhow::Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut lines = s.lines().collect::<Vec<_>>();
+ // see https://manpages.debian.org/testing/dpkg-dev/deb-changelog.5.en.html
+ // regexes adapted from /usr/share/perl5/Dpkg/Changelog/Entry/Debian.pm
+
+ let firstline = lines[0];
+ let re1 =
+ Regex::new(r"(?i)^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)((?:\s+[-+0-9a-z.]+)+);(.*?)\s*$")
+ .unwrap();
+ let matches1 = re1.captures(firstline).unwrap();
+ let mut i = 1;
+ while line_is_blank(lines[i]) {
+ i += 1;
+ }
+ lines = lines.split_off(i);
+
+ while line_is_blank(lines.last().unwrap()) {
+ lines.pop();
+ }
+ let lastline = lines.pop().unwrap();
+ while line_is_blank(lines.last().unwrap()) {
+ lines.pop();
+ }
+ let re2 = Regex::new(r"^ \-\- ((?:.*) <(?:.*)>) ?(\w.*\S)\s*$").unwrap();
+ let matches2 = re2.captures(lastline).unwrap();
+
+ Ok(Self::new(
+ matches1[1].to_string(),
+ matches1[2].to_string(),
+ matches1[3].to_string(),
+ matches1[4].to_string(),
+ matches2[1].to_string(),
+ DateTime::parse_from_rfc2822(&matches2[2])?,
+ lines.iter().map(|s| s.to_string()).collect(),
+ ))
+ }
+}
+
+impl ChangelogEntry {
+ pub fn new(
+ source: String,
+ version: String,
+ distribution: String,
+ options: String,
+ maintainer: String,
+ date: DateTime<FixedOffset>,
+ items: Vec<String>,
+ ) -> Self {
+ ChangelogEntry {
+ source,
+ version,
+ distribution,
+ options,
+ maintainer,
+ date,
+ items,
+ }
+ }
+
+ pub fn maintainer_name(self: &ChangelogEntry) -> String {
+ let re = Regex::new(r"^\s*(\S.*\S)\s*<.*>\s*$").unwrap();
+ let matches = re.captures(&self.maintainer).unwrap();
+ matches[1].to_string()
+ }
+
+ pub fn version_parts(self: &ChangelogEntry) -> (String, String) {
+ let re = Regex::new(r"^(.*)-([^-]*)$").unwrap();
+ let matches = re.captures(&self.version).unwrap();
+ (matches[1].to_string(), matches[2].to_string())
+ }
+
+ pub fn deb_version_suffix(self: &ChangelogEntry) -> String {
+ let re = Regex::new(r".*-([^-]*)$").unwrap();
+ re.captures(&self.version).unwrap()[1].to_string()
+ }
+
+ pub fn deb_version_suffix_bump(self: &ChangelogEntry) -> String {
+ let suf = self.deb_version_suffix();
+ let re = Regex::new(r"^((?:.*\D)?)(\d*)$").unwrap();
+ let matches = re.captures(&suf).unwrap();
+ if matches[2].is_empty() {
+ format!("{}.1", &matches[1])
+ } else {
+ format!(
+ "{}{}",
+ &matches[1],
+ (matches[2].parse::<u64>().unwrap() + 1)
+ )
+ }
+ }
+}
+
+pub struct ChangelogIterator<'a> {
+ input: &'a [u8],
+ index: usize,
+}
+
+impl<'a> ChangelogIterator<'a> {
+ pub fn from(input: &'a str) -> ChangelogIterator<'a> {
+ ChangelogIterator {
+ input: input.as_bytes(),
+ index: 0,
+ }
+ }
+}
+
+impl<'a> Iterator for ChangelogIterator<'a> {
+ type Item = &'a str;
+
+ #[inline]
+ fn next(&mut self) -> Option<&'a str> {
+ let slice = &self.input[self.index..];
+ if slice.is_empty() {
+ return None;
+ }
+ let mut result = slice;
+ // ghetto parser; also hack around the fact rust's str doesn't
+ // support proper indexing, boo
+ for (i, c) in slice.iter().enumerate() {
+ if *c != b'\n' {
+ continue;
+ }
+ if i < (slice.len() - 1) && (slice[i + 1] as char).is_whitespace() {
+ continue;
+ }
+ self.index += i + 1;
+ result = &slice[..=i];
+ break;
+ }
+ Some(str::from_utf8(result).unwrap())
+ }
+}
--- /dev/null
+use std::env::{self, VarError};
+use std::fmt::{self, Write};
+
+use anyhow::{format_err, Error};
+use semver::Version;
+use textwrap::fill;
+
+use crate::config::{self, Config, PackageKey};
+use crate::errors::*;
+
+pub struct Source {
+ name: String,
+ section: String,
+ priority: String,
+ maintainer: String,
+ uploaders: Vec<String>,
+ standards: String,
+ build_deps: Vec<String>,
+ vcs_git: String,
+ vcs_browser: String,
+ homepage: String,
+ crate_name: String,
+ requires_root: String,
+}
+
+pub struct Package {
+ name: String,
+ arch: String,
+ multi_arch: String,
+ section: Option<String>,
+ depends: Vec<String>,
+ recommends: Vec<String>,
+ suggests: Vec<String>,
+ provides: Vec<String>,
+ summary: Description,
+ description: Description,
+ extra_lines: Vec<String>,
+}
+
+pub struct Description {
+ pub prefix: String,
+ pub suffix: String,
+}
+
+pub struct PkgTest {
+ name: String,
+ crate_name: String,
+ feature: String,
+ version: String,
+ extra_test_args: Vec<String>,
+ depends: Vec<String>,
+ extra_restricts: Vec<String>,
+}
+
+impl fmt::Display for Source {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Source: {}", self.name)?;
+ writeln!(f, "Section: {}", self.section)?;
+ writeln!(f, "Priority: {}", self.priority)?;
+ writeln!(f, "Build-Depends: {}", self.build_deps.join(",\n "))?;
+ writeln!(f, "Maintainer: {}", self.maintainer)?;
+ if !self.uploaders.is_empty() {
+ writeln!(f, "Uploaders:\n {}", self.uploaders.join(",\n "))?;
+ }
+ writeln!(f, "Standards-Version: {}", self.standards)?;
+ writeln!(f, "Vcs-Git: {}", self.vcs_git)?;
+ writeln!(f, "Vcs-Browser: {}", self.vcs_browser)?;
+
+ if !self.homepage.is_empty() {
+ writeln!(f, "Homepage: {}", self.homepage)?;
+ }
+
+ // We used to set this conditionally, however it's best to do it
+ // unconditionally as some crates' names have a number suffix e.g.
+ // "utf-8". Without setting X-Cargo-Crate, we cannot distinguish:
+ // a) "utf" crate at version 8 with semver_suffix = true
+ // b) "utf-8" crate at version latest with semver_suffix = false.
+ // dh-cargo assumes (a) which is wrong for the "utf-8" crate
+ writeln!(f, "X-Cargo-Crate: {}", self.crate_name)?;
+ writeln!(f, "Rules-Requires-Root: {}", self.requires_root)?;
+
+ Ok(())
+ }
+}
+
+impl fmt::Display for Package {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Package: {}", self.name)?;
+ writeln!(f, "Architecture: {}", self.arch)?;
+ writeln!(f, "Multi-Arch: {}", self.multi_arch)?;
+ if let Some(section) = &self.section {
+ writeln!(f, "Section: {}", section)?;
+ }
+
+ if !self.depends.is_empty() {
+ writeln!(f, "Depends:\n {}", self.depends.join(",\n "))?;
+ }
+ if !self.recommends.is_empty() {
+ writeln!(f, "Recommends:\n {}", self.recommends.join(",\n "))?;
+ }
+ if !self.suggests.is_empty() {
+ writeln!(f, "Suggests:\n {}", self.suggests.join(",\n "))?;
+ }
+ if !self.provides.is_empty() {
+ writeln!(f, "Provides:\n {}", self.provides.join(",\n "))?;
+ }
+
+ for line in &self.extra_lines {
+ writeln!(f, "{}", line)?;
+ }
+
+ self.write_description(f)
+ }
+}
+
+impl fmt::Display for PkgTest {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let extra_args = if self.extra_test_args.is_empty() {
+ "".into()
+ } else {
+ format!(" {}", self.extra_test_args.join(" "))
+ };
+ writeln!(
+ f,
+ "Test-Command: /usr/share/cargo/bin/cargo-auto-test {} {} --all-targets{}",
+ self.crate_name, self.version, extra_args,
+ )?;
+ writeln!(f, "Features: test-name={}:{}", &self.name, &self.feature)?;
+ // TODO: drop the below workaround when rust-lang/cargo#5133 is fixed.
+ // The downside of our present work-around is that more dependencies
+ // must be installed, which makes it harder to actually run the tests
+ let cargo_bug_fixed = false;
+ let default_deps = if cargo_bug_fixed { &self.name } else { "@" };
+
+ let depends = if self.depends.is_empty() {
+ "".into()
+ } else {
+ format!(", {}", self.depends.join(", "))
+ };
+ writeln!(f, "Depends: dh-cargo (>= 18){}, {}", depends, default_deps)?;
+
+ let restricts = if self.extra_restricts.is_empty() {
+ "".into()
+ } else {
+ format!(", {}", self.extra_restricts.join(", "))
+ };
+ writeln!(
+ f,
+ "Restrictions: allow-stderr, skip-not-installable{}",
+ restricts,
+ )?;
+ Ok(())
+ }
+}
+
+impl Source {
+ pub fn pkg_prefix() -> &'static str {
+ if config::testing_ruzt() {
+ // avoid accidentally installing official packages during tests
+ "ruzt"
+ } else {
+ "rust"
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ basename: &str,
+ name_suffix: Option<&str>,
+ crate_name: &str,
+ home: &str,
+ lib: bool,
+ maintainer: String,
+ uploaders: Vec<String>,
+ build_deps: Vec<String>,
+ _requires_root: String,
+ ) -> Result<Source> {
+ let pkgbase = match name_suffix {
+ None => basename.to_string(),
+ Some(suf) => format!("{}{}", basename, suf),
+ };
+ let section = if lib {
+ "rust"
+ } else {
+ "FIXME-IN-THE-SOURCE-SECTION"
+ };
+ let priority = "optional".to_string();
+ let vcs_browser = format!(
+ "https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/{}",
+ pkgbase
+ );
+ let vcs_git = format!(
+ "https://salsa.debian.org/rust-team/debcargo-conf.git [src/{}]",
+ pkgbase
+ );
+ Ok(Source {
+ name: dsc_name(&pkgbase),
+ section: section.to_string(),
+ priority,
+ maintainer,
+ uploaders,
+ standards: "4.6.1".to_string(),
+ build_deps,
+ vcs_git,
+ vcs_browser,
+ homepage: home.to_string(),
+ crate_name: crate_name.to_string(),
+ requires_root: "no".to_string(),
+ })
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn apply_overrides(&mut self, config: &Config) {
+ if let Some(section) = config.section() {
+ self.section = section.to_string();
+ }
+
+ if let Some(policy) = config.policy_version() {
+ self.standards = policy.to_string();
+ }
+
+ self.build_deps.extend(
+ config
+ .build_depends()
+ .into_iter()
+ .flatten()
+ .map(String::to_string),
+ );
+ let bdeps_ex = config
+ .build_depends_excludes()
+ .map(Vec::as_slice)
+ .unwrap_or(&[]);
+ self.build_deps.retain(|x| !bdeps_ex.contains(x));
+
+ if let Some(homepage) = config.homepage() {
+ self.homepage = homepage.to_string();
+ }
+
+ if let Some(vcs_git) = config.vcs_git() {
+ self.vcs_git = vcs_git.to_string();
+ }
+
+ if let Some(vcs_browser) = config.vcs_browser() {
+ self.vcs_browser = vcs_browser.to_string();
+ }
+
+ if let Some(requires_root) = config.requires_root() {
+ self.requires_root = requires_root.to_string();
+ }
+ }
+}
+
+impl Package {
+ pub fn pkg_prefix() -> &'static str {
+ if config::testing_ruzt() {
+ // avoid accidentally installing official packages during tests
+ "libruzt"
+ } else {
+ "librust"
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn new(
+ basename: &str,
+ name_suffix: Option<&str>,
+ version: &Version,
+ summary: Description,
+ description: Description,
+ feature: Option<&str>,
+ f_deps: Vec<&str>,
+ o_deps: Vec<String>,
+ f_provides: Vec<&str>,
+ f_recommends: Vec<&str>,
+ f_suggests: Vec<&str>,
+ ) -> Result<Package> {
+ let pkgbase = match name_suffix {
+ None => basename.to_string(),
+ Some(suf) => format!("{}{}", basename, suf),
+ };
+ let deb_feature2 = &|p: &str, f: &str| {
+ format!(
+ "{} (= ${{binary:Version}})",
+ match f {
+ "" => deb_name(p),
+ _ => deb_feature_name(p, f),
+ }
+ )
+ };
+ let deb_feature = &|f: &str| deb_feature2(&pkgbase, f);
+
+ let filter_provides = &|x: Vec<&str>| {
+ x.into_iter()
+ .filter(|f| !f_provides.contains(f))
+ .map(deb_feature)
+ .collect()
+ };
+ let (recommends, suggests) = match feature {
+ Some(_) => (vec![], vec![]),
+ None => (filter_provides(f_recommends), filter_provides(f_suggests)),
+ };
+
+ // Provides for all possible versions, see:
+ // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901827#35
+ // https://wiki.debian.org/Teams/RustPackaging/Policy#Package_provides
+ let mut provides = vec![];
+ let version_suffixes = vec![
+ "".to_string(),
+ format!("-{}", version.major),
+ format!("-{}.{}", version.major, version.minor),
+ format!("-{}.{}.{}", version.major, version.minor, version.patch),
+ ];
+ for suffix in version_suffixes.iter() {
+ let p = format!("{}{}", basename, suffix);
+ provides.push(deb_feature2(&p, feature.unwrap_or("")));
+ provides.extend(f_provides.iter().map(|f| deb_feature2(&p, f)));
+ }
+ let provides_self = deb_feature(feature.unwrap_or(""));
+ // rust dropped Vec::remove_item for annoying reasons, the below is
+ // an unofficial recommended replacement from the RFC #40062
+ let i = provides.iter().position(|x| *x == *provides_self);
+ i.map(|i| provides.remove(i));
+
+ let mut depends = vec!["${misc:Depends}".to_string()];
+ if feature.is_some() && !f_deps.contains(&"") {
+ // in dh-cargo we symlink /usr/share/doc/{$feature => $main} pkg
+ // so we always need this direct dependency, even if the feature
+ // only indirectly depends on the bare library via another
+ depends.push(deb_feature(""));
+ }
+ depends.extend(f_deps.into_iter().map(deb_feature));
+ depends.extend(o_deps);
+
+ Ok(Package {
+ name: match feature {
+ None => deb_name(&pkgbase),
+ Some(f) => deb_feature_name(&pkgbase, f),
+ },
+ arch: "any".to_string(),
+ // This is the best but not ideal option for us.
+ //
+ // Currently Debian M-A spec has a deficiency where a package X that
+ // build-depends on a (M-A:foreign+arch:all) package that itself
+ // depends on an arch:any package Z, will pick up the BUILD_ARCH of
+ // package Z instead of the HOST_ARCH. This is because we currently
+ // have no way of telling dpkg to use HOST_ARCH when checking that the
+ // dependencies of Y are satisfied, which is done at install-time
+ // without any knowledge that we're about to do a cross-compile. It
+ // is also problematic to tell dpkg to "accept any arch" because of
+ // the presence of non-M-A:same packages in the archive, that are not
+ // co-installable - different arches of Z might be depended-upon by
+ // two conflicting chains. (dpkg has so far chosen not to add an
+ // exception for the case where package Z is M-A:same co-installable).
+ //
+ // The recommended work-around for now from the dpkg developers is to
+ // make our packages arch:any M-A:same even though this results in
+ // duplicate packages in the Debian archive. For very large crates we
+ // will eventually want to make debcargo generate -data packages that
+ // are arch:all and have the arch:any -dev packages depend on it.
+ multi_arch: "same".to_string(),
+ section: None,
+ depends,
+ recommends,
+ suggests,
+ provides,
+ summary,
+ description,
+ extra_lines: match (name_suffix, feature) {
+ (Some(_), None) => {
+ let fullpkg = format!("{}-{}", basename, version);
+ vec![
+ format!("Replaces: {}", deb_name(&fullpkg)),
+ format!("Breaks: {}", deb_name(&fullpkg)),
+ ]
+ }
+ (_, _) => vec![],
+ },
+ })
+ }
+
+ pub fn new_bin(
+ basename: &str,
+ name_suffix: Option<&str>,
+ section: Option<&str>,
+ summary: Description,
+ description: Description,
+ ) -> Self {
+ let (name, mut provides) = match name_suffix {
+ None => (basename.to_string(), vec![]),
+ Some(suf) => (
+ format!("{}{}", basename, suf),
+ vec![format!("{} (= ${{binary:Version}})", basename)],
+ ),
+ };
+ provides.push("${cargo:Provides}".to_string());
+ Package {
+ name,
+ arch: "any".to_string(),
+ multi_arch: "allowed".to_string(),
+ section: section.map(|s| s.to_string()),
+ depends: vec![
+ "${misc:Depends}".to_string(),
+ "${shlibs:Depends}".to_string(),
+ "${cargo:Depends}".to_string(),
+ ],
+ recommends: vec!["${cargo:Recommends}".to_string()],
+ suggests: vec!["${cargo:Suggests}".to_string()],
+ provides,
+ summary,
+ description,
+ extra_lines: vec![
+ "Built-Using: ${cargo:Built-Using}".to_string(),
+ "XB-X-Cargo-Built-Using: ${cargo:X-Cargo-Built-Using}".to_string(),
+ ],
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ self.name.as_str()
+ }
+
+ fn write_description(&self, out: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(out, "Description: {}", &self.summary)?;
+ let description = format!("{}", &self.description);
+ for line in fill(description.trim(), 79).lines() {
+ let line = line.trim_end();
+ if line.is_empty() {
+ writeln!(out, " .")?;
+ } else if line.starts_with("- ") {
+ writeln!(out, " {}", line)?;
+ } else {
+ writeln!(out, " {}", line)?;
+ }
+ }
+ Ok(())
+ }
+
+ #[allow(clippy::result_unit_err)]
+ pub fn summary_check_len(&self) -> std::result::Result<(), ()> {
+ if self.summary.prefix.len() <= 80 {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ pub fn apply_overrides(&mut self, config: &Config, key: PackageKey, f_provides: Vec<&str>) {
+ if let Some(section) = config.package_section(key) {
+ self.section = Some(section.to_string());
+ }
+ self.summary
+ .apply_overrides(&config.summary, config.package_summary(key));
+ self.description
+ .apply_overrides(&config.description, config.package_description(key));
+
+ self.depends.extend(config::package_field_for_feature(
+ &|x| config.package_depends(x),
+ key,
+ &f_provides,
+ ));
+ self.recommends.extend(config::package_field_for_feature(
+ &|x| config.package_recommends(x),
+ key,
+ &f_provides,
+ ));
+ self.suggests.extend(config::package_field_for_feature(
+ &|x| config.package_suggests(x),
+ key,
+ &f_provides,
+ ));
+ self.provides.extend(config::package_field_for_feature(
+ &|x| config.package_provides(x),
+ key,
+ &f_provides,
+ ));
+
+ self.extra_lines.extend(
+ config
+ .package_extra_lines(key)
+ .into_iter()
+ .flatten()
+ .map(|s| s.to_string()),
+ );
+ }
+}
+
+impl Description {
+ fn apply_overrides(&mut self, global: &Option<String>, per_package: Option<&str>) {
+ if let Some(per_package) = per_package {
+ self.prefix = per_package.to_string();
+ self.suffix = "".to_string();
+ } else if let Some(global) = &global {
+ self.prefix = global.into();
+ }
+ }
+}
+impl fmt::Display for Description {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}{}", &self.prefix, self.suffix)
+ }
+}
+
+impl PkgTest {
+ pub fn new(
+ name: &str,
+ crate_name: &str,
+ feature: &str,
+ version: &str,
+ extra_test_args: Vec<&str>,
+ depends: &[String],
+ extra_restricts: Vec<&str>,
+ ) -> Result<PkgTest> {
+ Ok(PkgTest {
+ name: name.to_string(),
+ crate_name: crate_name.to_string(),
+ feature: feature.to_string(),
+ version: version.to_string(),
+ extra_test_args: extra_test_args.iter().map(|x| x.to_string()).collect(),
+ depends: depends.to_vec(),
+ extra_restricts: extra_restricts.iter().map(|x| x.to_string()).collect(),
+ })
+ }
+}
+
+/// Translates a semver into a Debian-format upstream version.
+/// Omits the build metadata, and uses a ~ before the prerelease version so it
+/// compares earlier than the subsequent release.
+pub fn deb_upstream_version(v: &Version) -> String {
+ let mut s = format!("{}.{}.{}", v.major, v.minor, v.patch);
+ if !v.pre.is_empty() {
+ write!(s, "~{}", v.pre.as_str()).unwrap();
+ }
+ s
+}
+
+pub fn base_deb_name(crate_name: &str) -> String {
+ crate_name.replace('_', "-").to_lowercase()
+}
+
+pub fn dsc_name(name: &str) -> String {
+ format!("{}-{}", Source::pkg_prefix(), base_deb_name(name))
+}
+
+pub fn deb_name(name: &str) -> String {
+ format!("{}-{}-dev", Package::pkg_prefix(), base_deb_name(name))
+}
+
+pub fn deb_feature_name(name: &str, feature: &str) -> String {
+ format!(
+ "{}-{}+{}-dev",
+ Package::pkg_prefix(),
+ base_deb_name(name),
+ base_deb_name(feature)
+ )
+}
+
+/// Retrieve one of a series of environment variables, and provide a friendly error message for
+/// non-UTF-8 values.
+fn get_envs(keys: &[&str]) -> Result<Option<String>> {
+ for key in keys {
+ match env::var(key) {
+ Ok(val) => {
+ return Ok(Some(val));
+ }
+ Err(e @ VarError::NotUnicode(_)) => {
+ return Err(Error::from(e)
+ .context(format!("Environment variable ${} not valid UTF-8", key)));
+ }
+ Err(VarError::NotPresent) => {}
+ }
+ }
+ Ok(None)
+}
+
+/// Determine a name and email address from environment variables.
+pub fn get_deb_author() -> Result<String> {
+ let name = get_envs(&["DEBFULLNAME", "NAME"])?.ok_or_else(|| {
+ format_err!("Unable to determine your name; please set $DEBFULLNAME or $NAME")
+ })?;
+ let email = get_envs(&["DEBEMAIL", "EMAIL"])?.ok_or_else(|| {
+ format_err!("Unable to determine your email; please set $DEBEMAIL or $EMAIL")
+ })?;
+ Ok(format!("{} <{}>", name, email))
+}
+
+#[cfg(test)]
+mod tests;
--- /dev/null
+use super::PkgTest;
+
+struct PkgTestFmtData<'a> {
+ feature: &'a str,
+ extra_test_args: Vec<&'a str>,
+ depends: Vec<String>,
+ extra_restricts: Vec<&'a str>,
+ expected: &'a str,
+}
+
+#[test]
+fn pkgtest_fmt_has_no_extra_whitespace() {
+ let checks = vec![
+ PkgTestFmtData {
+ feature: "",
+ extra_test_args: Vec::new(),
+ depends: Vec::new(),
+ extra_restricts: Vec::new(),
+ expected: r"Test-Command: /usr/share/cargo/bin/cargo-auto-test crate 1.0 --all-targets
+Features: test-name=librust-crate-dev:
+Depends: dh-cargo (>= 18), @
+Restrictions: allow-stderr, skip-not-installable
+",
+ },
+ PkgTestFmtData {
+ feature: "X",
+ extra_test_args: vec!["--no-default-features", "--features X"],
+ depends: vec!["libfoo-dev".into(), "bar".into()],
+ extra_restricts: vec!["flaky"],
+ expected: r"Test-Command: /usr/share/cargo/bin/cargo-auto-test crate 1.0 --all-targets --no-default-features --features X
+Features: test-name=librust-crate-dev:X
+Depends: dh-cargo (>= 18), libfoo-dev, bar, @
+Restrictions: allow-stderr, skip-not-installable, flaky
+",
+ },
+ ];
+
+ for check in checks {
+ let pkgtest = PkgTest::new(
+ "librust-crate-dev",
+ "crate",
+ check.feature,
+ "1.0",
+ check.extra_test_args,
+ &check.depends,
+ check.extra_restricts,
+ )
+ .unwrap();
+
+ assert_eq!(check.expected, &pkgtest.to_string());
+ }
+}
--- /dev/null
+use cargo::core::manifest;
+use chrono::{DateTime, Datelike, NaiveDateTime, Utc};
+use git2::Repository;
+use regex;
+use tempfile;
+use textwrap::fill;
+use walkdir;
+
+use std::cmp::Ordering;
+use std::collections::BTreeMap;
+use std::env;
+use std::fmt;
+use std::fs;
+use std::io::{BufRead, BufReader, Read};
+use std::path::Path;
+
+use crate::errors::*;
+
+const DEB_COPYRIGHT_FORMAT: &str = "\
+ https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/";
+
+macro_rules! format_para {
+ ($fmt: expr, $text:expr) => {{
+ for line in $text.lines() {
+ let line = line.trim_end();
+ if line.is_empty() {
+ writeln!($fmt, " .")?;
+ } else {
+ writeln!($fmt, " {}", line)?;
+ }
+ }
+ write!($fmt, "")
+ }};
+}
+
+struct UpstreamInfo {
+ name: String,
+ contacts: Vec<String>,
+ source: String,
+}
+
+#[derive(Clone)]
+pub struct Files {
+ files: String,
+ copyright: Vec<String>,
+ license: String,
+ comment: String,
+}
+
+#[derive(Clone)]
+struct License {
+ name: String,
+ text: String,
+}
+
+pub struct DebCopyright {
+ format: String,
+ upstream: UpstreamInfo,
+ files: Vec<Files>,
+ licenses: Vec<License>,
+}
+
+impl fmt::Display for DebCopyright {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Format: {}", self.format)?;
+ write!(f, "{}", self.upstream)?;
+
+ for file in &self.files {
+ write!(f, "\n{}", file)?;
+ }
+
+ for license in &self.licenses {
+ write!(f, "\n{}", license)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl DebCopyright {
+ fn new(u: UpstreamInfo, f: &[Files], l: &[License]) -> DebCopyright {
+ DebCopyright {
+ format: DEB_COPYRIGHT_FORMAT.to_string(),
+ upstream: u,
+ files: f.to_vec(),
+ licenses: l.to_vec(),
+ }
+ }
+}
+
+impl fmt::Display for UpstreamInfo {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Upstream-Name: {}", self.name)?;
+ write!(f, "Upstream-Contact:")?;
+ if self.contacts.len() > 1 {
+ writeln!(f)?;
+ }
+ for contact in &self.contacts {
+ writeln!(f, " {}", contact)?;
+ }
+ if !self.source.is_empty() {
+ writeln!(f, "Source: {}", self.source)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl UpstreamInfo {
+ fn new(name: String, authors: &[String], repo: &str) -> UpstreamInfo {
+ assert!(!authors.is_empty());
+ UpstreamInfo {
+ name,
+ contacts: authors.to_vec(),
+ source: repo.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for Files {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Files: {}", self.files)?;
+ write!(f, "Copyright:")?;
+ if self.copyright.len() > 1 {
+ writeln!(f)?;
+ }
+ for copyright in &self.copyright {
+ writeln!(f, " {}", copyright)?;
+ }
+ writeln!(f, "License: {}", self.license)?;
+ if !self.comment.is_empty() {
+ writeln!(f, "Comment:")?;
+ format_para!(f, &self.comment)?;
+ }
+ Ok(())
+ }
+}
+
+impl Files {
+ pub fn new<T: ToString>(name: &str, notice: &[T], license: &str, comment: &str) -> Files {
+ assert!(!notice.is_empty());
+ Files {
+ files: name.to_string(),
+ copyright: notice.iter().map(|s| s.to_string()).collect(),
+ license: license.to_string(),
+ comment: comment.to_string(),
+ }
+ }
+
+ pub fn files(&self) -> &str {
+ &self.files
+ }
+
+ pub fn license(&self) -> &str {
+ &self.license
+ }
+}
+
+impl fmt::Display for License {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "License: {}", self.name)?;
+ format_para!(f, &self.text)?;
+ Ok(())
+ }
+}
+
+impl License {
+ fn new(name: String, text: String) -> License {
+ License { name, text }
+ }
+}
+
+macro_rules! default_files {
+ ($file:expr, $notice:expr) => {{
+ let comment = concat!(
+ "FIXME (overlay): These notices are extracted from files. Please ",
+ "review them before uploading to the archive."
+ );
+ Files::new(
+ $file,
+ $notice,
+ "UNKNOWN-LICENSE; FIXME (overlay)",
+ &fill(comment, 79),
+ )
+ }};
+}
+
+fn gen_files(debsrcdir: &Path) -> Result<Vec<Files>> {
+ let mut copyright_notices = BTreeMap::new();
+
+ let copyright_notice_re =
+ regex::Regex::new(r"(?:[Cc]opyright|©)(?:\s|[©:,()Cc<])*\b(\d{4}\b.*)$")?;
+
+ // Get current working directory and move inside the extracted source of
+ // crate. This is necessary so as to capture correct path for files in
+ // debian/copyright.
+ let current_dir = env::current_dir()?;
+ env::set_current_dir(debsrcdir)?;
+
+ // Here we specifically use "." to avoid absolute paths. If we use
+ // current_dir then we end up having absolute path from user home directory,
+ // which again messes debian/copyright.
+ // Use of . creates paths in format ./src/, so we strip the leading "./"
+ for entry in walkdir::WalkDir::new(".").sort_by(|a, b| a.file_name().cmp(b.file_name())) {
+ let entry = entry?;
+ if entry.file_type().is_file() {
+ let copyright_file = entry
+ .path()
+ .strip_prefix("./")?
+ .to_str()
+ .unwrap()
+ .to_string();
+ let file = fs::File::open(entry.path())?;
+ let reader = BufReader::new(file);
+ for line in reader.lines() {
+ if let Ok(line) = line {
+ if let Some(m) = copyright_notice_re.captures(&line) {
+ let m = m.get(1).unwrap();
+ let start = m.start();
+ let end = m.end();
+ let notice = line[start..end]
+ .trim_end()
+ .trim_end_matches(". See the COPYRIGHT")
+ .to_string();
+ if !copyright_notices.contains_key(©right_file) {
+ copyright_notices.insert(copyright_file.clone(), vec![]);
+ }
+ copyright_notices
+ .get_mut(©right_file)
+ .unwrap()
+ .push(notice);
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ // Restore back to original working directory as we can continue without
+ // problems.
+ env::set_current_dir(current_dir.as_path())?;
+
+ let mut notices: Vec<Files> = Vec::new();
+ for (filename, notice) in ©right_notices {
+ notices.push(default_files!(filename, notice));
+ }
+
+ Ok(notices)
+}
+
+fn get_licenses(license: &str) -> Result<Vec<License>> {
+ let mut licenses = BTreeMap::new();
+ let sep = regex::Regex::new(r"(?i:(\s(or|and)\s|/))")?;
+ let known_licenses = vec![
+ ("agpl-3.0", include_str!("licenses/AGPL-3.0")),
+ ("apache-2.0", include_str!("licenses/Apache-2.0")),
+ ("bsd-2-clause", include_str!("licenses/BSD-2-Clause")),
+ ("bsd-3-clause", include_str!("licenses/BSD-3-Clause")),
+ ("cc0-1.0", include_str!("licenses/CC0-1.0")),
+ ("gpl-2.0", include_str!("licenses/GPL-2.0")),
+ ("gpl-3.0", include_str!("licenses/GPL-3.0")),
+ ("isc", include_str!("licenses/ISC")),
+ ("lgpl-2.0", include_str!("licenses/LGPL-2.0")),
+ ("lgpl-2.1", include_str!("licenses/LGPL-2.1")),
+ ("lgpl-3.0", include_str!("licenses/LGPL-3.0")),
+ ("mit", include_str!("licenses/MIT")),
+ ("mitnfa", include_str!("licenses/MITNFA")),
+ ("mpl-1.1", include_str!("licenses/MPL-1.1")),
+ ("mpl-2.0", include_str!("licenses/MPL-2.0")),
+ ("unlicense", include_str!("licenses/Unlicense")),
+ ("zlib", include_str!("licenses/Zlib")),
+ ]
+ .into_iter()
+ .collect::<BTreeMap<_, _>>();
+
+ let lses: Vec<&str> = sep.split(license).filter(|s| !s.is_empty()).collect();
+ for ls in lses {
+ let lname = ls
+ .trim()
+ .to_lowercase()
+ .trim_end_matches('+')
+ .trim_end_matches("-or-later")
+ .to_string();
+ let text = match known_licenses.get(lname.as_str()) {
+ Some(s) => s.to_string(),
+ None => "FIXME (overlay): Unrecognized crate license, please find the \
+ full license text in the rest of the crate source code and \
+ copy-paste it here"
+ .to_string(),
+ };
+ licenses.insert(ls.trim().to_string(), text);
+ }
+
+ let mut lblocks: Vec<License> = Vec::new();
+ if !licenses.is_empty() {
+ for (l, t) in licenses {
+ lblocks.push(License::new(l, t));
+ }
+ }
+
+ Ok(lblocks)
+}
+
+fn copyright_fromgit(repo_url: &str) -> Result<String> {
+ let tempdir = tempfile::Builder::new()
+ .prefix("debcargo")
+ .tempdir_in(".")?;
+ let repo = Repository::clone(repo_url, tempdir.path())?;
+
+ let mut revwalker = repo.revwalk()?;
+ revwalker.push_head()?;
+
+ // Get the latest and first commit id. This is bit ugly
+ let latest_id = revwalker.next().unwrap()?;
+ let first_id = revwalker.last().unwrap()?; // revwalker ends here is consumed by last
+
+ let first_commit = repo.find_commit(first_id)?;
+ let latest_commit = repo.find_commit(latest_id)?;
+
+ let first_year = DateTime::<Utc>::from_utc(
+ NaiveDateTime::from_timestamp_opt(first_commit.time().seconds(), 0)
+ .ok_or(anyhow::Error::msg("lo"))?,
+ Utc,
+ )
+ .year();
+
+ let latest_year = DateTime::<Utc>::from_utc(
+ NaiveDateTime::from_timestamp_opt(latest_commit.time().seconds(), 0)
+ .ok_or(anyhow::Error::msg("lo"))?,
+ Utc,
+ )
+ .year();
+
+ let notice = match first_year.cmp(&latest_year) {
+ Ordering::Equal => format!("{}", first_year),
+ _ => format!("{}-{},", first_year, latest_year),
+ };
+
+ Ok(notice)
+}
+
+pub fn debian_copyright(
+ srcdir: &Path,
+ manifest: &manifest::Manifest,
+ manifest_path: &Path,
+ maintainer: &str,
+ uploaders: &[&str],
+ year_range: (i32, i32),
+ guess_harder: bool,
+) -> Result<DebCopyright> {
+ let meta = manifest.metadata().clone();
+ let repository = match meta.repository {
+ None => "",
+ Some(ref r) => r,
+ };
+
+ // The Authors field is optional according to
+ // https://rust-lang.github.io/rfcs/3052-optional-authors-field.html
+ // and crates.io publishes crates without the field already.
+ let unknown_authors = vec!["FIXME (overlay) UNKNOWN-AUTHORS".to_string()];
+ let authors = if meta.authors.is_empty() {
+ &unknown_authors
+ } else {
+ &meta.authors
+ };
+
+ let upstream = UpstreamInfo::new(manifest.name().to_string(), authors, repository);
+
+ let mut licenses: Vec<License> = Vec::new();
+ let mut crate_license: String = "".to_string();
+
+ if let Some(ref license_file_name) = meta.license_file {
+ let license_file = manifest_path.with_file_name(license_file_name);
+ let mut text = Vec::new();
+ fs::File::open(license_file)?.read_to_end(&mut text)?;
+ licenses.reserve(1);
+ let stext = String::from_utf8(text)?;
+ licenses.push(License::new(
+ "UNKNOWN-LICENSE; FIXME (overlay)".to_string(),
+ stext,
+ ));
+ } else if let Some(ref license) = meta.license {
+ licenses = get_licenses(license).unwrap();
+ crate_license = license
+ .trim()
+ .replace("/", " or ")
+ .replace(" OR ", " or ")
+ .replace(" AND ", " and ");
+ } else {
+ debcargo_bail!("Crate has no license or license_file");
+ }
+
+ let mut files = gen_files(srcdir)?;
+
+ let (y0, y1) = year_range;
+ let years = if y0 == y1 {
+ format!("{}", y0)
+ } else {
+ format!("{}-{}", y0, y1)
+ };
+ let mut deb_notice = vec![format!("{} {}", years, maintainer)];
+ deb_notice.extend(uploaders.iter().map(|s| format!("{} {}", years, s)));
+ files.push(Files::new("debian/*", &deb_notice, &crate_license, ""));
+
+ // Insert catch all block as the first block of copyright file. Capture
+ // copyright notice from git log of the upstream repository.
+ let years = if guess_harder && !repository.is_empty() {
+ match copyright_fromgit(repository) {
+ Ok(x) => x,
+ Err(e) => {
+ debcargo_warn!(
+ "Failed to generate d/copyright from git repository {}: {}\n",
+ repository,
+ e
+ );
+ "FIXME (overlay) UNKNOWN-YEARS".to_string()
+ }
+ }
+ } else {
+ "FIXME (overlay) UNKNOWN-YEARS".to_string()
+ };
+ let notice = match meta.authors.len() {
+ 0 => vec![format!("FIXME (overlay) UNKNOWN-AUTHORS {}", years)],
+ 1 => vec![format!("{} {}", years, &meta.authors[0])],
+ _ => meta
+ .authors
+ .iter()
+ .map(|s| format!("{} {}", years, s))
+ .collect(),
+ };
+ let comment = concat!(
+ "FIXME (overlay): Since upstream copyright years are not available ",
+ "in Cargo.toml, they were extracted from the upstream Git ",
+ "repository. This may not be correct information so you should ",
+ "review and fix this before uploading to the archive."
+ );
+ files.insert(
+ 0,
+ Files::new("*", notice.as_slice(), &crate_license, &fill(comment, 79)),
+ );
+
+ Ok(DebCopyright::new(upstream, &files, &licenses))
+}
+
+#[cfg(test)]
+mod tests;
--- /dev/null
+use super::{debian_copyright, get_licenses};
+
+use std::path::Path;
+use std::rc::Rc;
+
+use cargo::{
+ core::{package::Package, SourceId},
+ util::{config::Config, toml::TomlManifest},
+};
+use toml::toml;
+
+#[test]
+fn check_get_licenses() {
+ let test_data: &[(&str, &[(&str, bool)])] = &[
+ ("AGPL-3.0", &[("AGPL-3.0", true)]),
+ ("AcmeCorp-1.0", &[("AcmeCorp-1.0", false)]),
+ ("AGPL-3.0-or-later", &[("AGPL-3.0-or-later", true)]),
+ ("Apache-2.0/MIT", &[("Apache-2.0", true), ("MIT", true)]),
+ ("Apache-2.0 or MIT", &[("Apache-2.0", true), ("MIT", true)]),
+ (
+ "FooBar-1.0 AND MIT",
+ &[("FooBar-1.0", false), ("MIT", true)],
+ ),
+ ];
+ for (name, expected) in test_data {
+ let licenses = get_licenses(name).expect("getting licenses failed");
+ let found: Vec<_> = licenses
+ .iter()
+ .map(|l| (l.name.as_str(), !l.text.starts_with("FIXME")))
+ .collect();
+ assert_eq!(&found[..], &expected[..]);
+ }
+}
+
+#[test]
+fn check_debian_copyright_authors() {
+ let checks = vec![
+ (
+ vec![],
+ vec!["FIXME (overlay) UNKNOWN-AUTHORS FIXME (overlay) UNKNOWN-YEARS"],
+ ),
+ (
+ vec!["Jordan Doe"],
+ vec!["FIXME (overlay) UNKNOWN-YEARS Jordan Doe"],
+ ),
+ (
+ vec!["Jordan Doe", "Jane Doe"],
+ vec![
+ "FIXME (overlay) UNKNOWN-YEARS Jordan Doe",
+ "FIXME (overlay) UNKNOWN-YEARS Jane Doe",
+ ],
+ ),
+ ];
+
+ for (input, expected_output) in checks.into_iter() {
+ let package = build_package_with_authors(input);
+ let srcdir = tempfile::tempdir().unwrap();
+ let copyright = debian_copyright(
+ srcdir.path(),
+ package.manifest(),
+ package.manifest_path(),
+ "Jordan Doe",
+ &[],
+ (2000, 2020),
+ false,
+ )
+ .unwrap();
+ let mut generated = false;
+ for file in ©right.files {
+ if file.files == "*" {
+ assert_eq!(file.copyright, expected_output);
+ generated = true;
+ }
+ }
+ assert!(generated);
+ }
+}
+
+fn build_package_with_authors(authors: Vec<&str>) -> Package {
+ let authors: Vec<String> = authors.into_iter().map(|s| s.to_string()).collect();
+ let toml = toml! {
+ [package]
+ name = "mypackage"
+ version = "1.2.3"
+ authors = authors
+ license = "AGPLv3"
+ };
+ let toml_manifest: Rc<TomlManifest> =
+ Rc::new(toml::from_str(&toml::to_string(&toml).unwrap()).unwrap());
+ let source_id = SourceId::for_path(Path::new("/path/to/mypackage")).unwrap();
+ let package_root = Path::new("/path/to/mypackage");
+ let config = Config::default().unwrap();
+ let manifest = TomlManifest::to_real_manifest(&toml_manifest, source_id, package_root, &config)
+ .unwrap()
+ .0;
+ Package::new(manifest, Path::new("/path/to/manifest"))
+}
--- /dev/null
+use cargo::core::Dependency;
+use itertools::Itertools;
+
+use std::cmp;
+use std::fmt;
+
+use crate::config::{testing_ignore_debpolv, Config};
+use crate::debian::{self, control::base_deb_name, Package};
+use crate::errors::*;
+
+#[derive(Eq, Clone)]
+#[allow(clippy::upper_case_acronyms)]
+enum V {
+ M(u64),
+ MM(u64, u64),
+ MMP(u64, u64, u64),
+}
+
+impl V {
+ fn new(p: &semver::Comparator) -> Result<Self> {
+ use self::V::*;
+ let mmp = match (p.minor, p.patch) {
+ (None, None) => M(p.major),
+ (Some(minor), None) => MM(p.major, minor),
+ (Some(minor), Some(patch)) => MMP(p.major, minor, patch),
+ (None, Some(_)) => debcargo_bail!("semver had patch without minor"),
+ };
+ Ok(mmp)
+ }
+
+ fn inclast(&self) -> V {
+ use self::V::*;
+ match *self {
+ M(major) => M(major + 1),
+ MM(major, minor) => MM(major, minor + 1),
+ MMP(major, minor, patch) => MMP(major, minor, patch + 1),
+ }
+ }
+
+ fn mmp(&self) -> (u64, u64, u64) {
+ use self::V::*;
+ match *self {
+ M(major) => (major, 0, 0),
+ MM(major, minor) => (major, minor, 0),
+ MMP(major, minor, patch) => (major, minor, patch),
+ }
+ }
+}
+
+impl cmp::Ord for V {
+ fn cmp(&self, other: &V) -> cmp::Ordering {
+ self.partial_cmp(other).unwrap()
+ }
+}
+
+impl cmp::PartialOrd for V {
+ fn partial_cmp(&self, other: &V) -> Option<cmp::Ordering> {
+ self.mmp().partial_cmp(&other.mmp())
+ }
+}
+
+impl cmp::PartialEq for V {
+ fn eq(&self, other: &V) -> bool {
+ self.mmp() == other.mmp()
+ }
+}
+
+impl fmt::Display for V {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::V::*;
+ match *self {
+ M(major) => write!(f, "{}", major),
+ MM(major, minor) => write!(f, "{}.{}", major, minor),
+ MMP(major, minor, patch) => write!(f, "{}.{}.{}", major, minor, patch),
+ }
+ }
+}
+
+struct VRange {
+ ge: Option<V>,
+ lt: Option<V>,
+}
+
+impl VRange {
+ fn new() -> Self {
+ VRange { ge: None, lt: None }
+ }
+
+ fn constrain_ge(&mut self, ge: V) -> &Self {
+ match self.ge {
+ Some(ref ge_) if &ge < ge_ => (),
+ _ => self.ge = Some(ge),
+ };
+ self
+ }
+
+ fn constrain_lt(&mut self, lt: V) -> &Self {
+ match self.lt {
+ Some(ref lt_) if < >= lt_ => (),
+ _ => self.lt = Some(lt),
+ };
+ self
+ }
+
+ fn to_deb_or_clause(&self, base: &str, suffix: &str) -> Result<String> {
+ use debian::dependency::V::*;
+ match (&self.ge, &self.lt) {
+ (None, None) => Ok(format!("{}{}", base, suffix)),
+ (Some(ge), None) => Ok(format!("{}{} (>= {}-~~)", base, suffix, ge)),
+ (None, Some(lt)) => Ok(format!("{}{} (<< {}-~~)", base, suffix, lt)),
+ (Some(ge), Some(lt)) => {
+ if ge >= lt {
+ debcargo_bail!("bad version range: >= {}, << {}", ge, lt);
+ }
+ let mut ranges = vec![];
+ let (lt_maj, lt_min, lt_pat) = lt.mmp();
+ let (ge_maj, ge_min, ge_pat) = ge.mmp();
+ if ge_maj < lt_maj {
+ ranges.push((M(ge_maj), Some((true, ge))));
+ ranges.extend((ge_maj + 1..lt_maj).map(|maj| (M(maj), None)));
+ ranges.push((M(lt_maj), Some((false, lt))));
+ } else {
+ assert!(ge_maj == lt_maj);
+ if ge_min < lt_min {
+ ranges.push((MM(ge_maj, ge_min), Some((true, ge))));
+ ranges.extend((ge_min + 1..lt_min).map(|min| (MM(ge_maj, min), None)));
+ ranges.push((MM(lt_maj, lt_min), Some((false, lt))));
+ } else {
+ assert!(ge_min == lt_min);
+ ranges.push((MMP(ge_maj, ge_min, ge_pat), Some((true, ge))));
+ ranges.extend(
+ (ge_pat + 1..lt_pat).map(|pat| (MMP(ge_maj, ge_min, pat), None)),
+ );
+ ranges.push((MMP(lt_maj, lt_min, lt_pat), Some((false, lt))));
+ }
+ };
+ // reverse the order so higher versions go first
+ // this helps sbuild find build-deps, it does not resolve alternatives by default
+ Ok(ranges
+ .iter()
+ .rev()
+ .filter_map(|(ver, cons)| match cons {
+ None => Some(format!("{}-{}{}", base, ver, suffix)),
+ Some((true, c)) => {
+ if c == &ver {
+ // A-x >= x is redundant, drop the >=
+ Some(format!("{}-{}{}", base, ver, suffix))
+ } else {
+ Some(format!("{}-{}{} (>= {}-~~)", base, ver, suffix, c))
+ }
+ }
+ Some((false, c)) => {
+ if c == &ver {
+ // A-x << x is unsatisfiable, drop it
+ None
+ } else {
+ Some(format!("{}-{}{} (<< {}-~~)", base, ver, suffix, c))
+ }
+ }
+ })
+ .join(" | "))
+ }
+ }
+ }
+}
+
+fn coerce_unacceptable_predicate<'a>(
+ dep: &Dependency,
+ p: &'a semver::Comparator,
+ allow_prerelease_deps: bool,
+) -> Result<&'a semver::Op> {
+ let mmp = &V::new(p)?;
+
+ // Cargo/semver and Debian handle pre-release versions quite
+ // differently, so a versioned Debian dependency cannot properly
+ // handle pre-release crates. This might be OK most of the time,
+ // coerce it to the non-pre-release version.
+ if !p.pre.is_empty() {
+ if allow_prerelease_deps || testing_ignore_debpolv() {
+ debcargo_warn!(
+ "Coercing removal of prerelease part of dependency: {} {:?}",
+ dep.package_name(),
+ p
+ )
+ } else {
+ debcargo_bail!(
+ "Cannot represent prerelease part of dependency: {} {:?}",
+ dep.package_name(),
+ p
+ )
+ }
+ }
+
+ use debian::dependency::V::M;
+ use semver::Op::*;
+ match (&p.op, mmp) {
+ (&Greater, &M(0)) => Ok(&p.op),
+ (&GreaterEq, &M(0)) => {
+ debcargo_warn!(
+ "Coercing unrepresentable dependency version predicate 'GtEq 0' to 'Gt 0': {} {:?}",
+ dep.package_name(),
+ p
+ );
+ Ok(&Greater)
+ }
+ // TODO: This will prevent us from handling wildcard dependencies with
+ // 0.0.0* so for now commenting this out.
+ // (_, &M(0)) => debcargo_bail!(
+ // "Unrepresentable dependency version predicate: {} {:?}",
+ // dep.package_name(),
+ // p
+ // ),
+ (_, _) => Ok(&p.op),
+ }
+}
+
+fn generate_version_constraints(
+ vr: &mut VRange,
+ dep: &Dependency,
+ p: &semver::Comparator,
+ op: &semver::Op,
+) -> Result<()> {
+ let mmp = V::new(p)?;
+ use debian::dependency::V::*;
+ use semver::Op::*;
+ // see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
+ // and https://docs.rs/semver/1/semver/enum.Op.html for semantics
+ match (*op, &mmp) {
+ (Less, &M(0)) | (Less, &MM(0, 0)) | (Less, &MMP(0, 0, 0)) => debcargo_bail!(
+ "Unrepresentable dependency version predicate: {} {:?}",
+ dep.package_name(),
+ p
+ ),
+ (Less, _) => {
+ vr.constrain_lt(mmp);
+ }
+ (LessEq, _) => {
+ vr.constrain_lt(mmp.inclast());
+ }
+ (Greater, _) => {
+ vr.constrain_ge(mmp.inclast());
+ }
+ (GreaterEq, _) => {
+ vr.constrain_ge(mmp);
+ }
+ (Exact, _) | (Wildcard, _) => {
+ vr.constrain_lt(mmp.inclast());
+ vr.constrain_ge(mmp);
+ }
+ (Tilde, &M(_)) | (Tilde, &MM(_, _)) => {
+ vr.constrain_lt(mmp.inclast());
+ vr.constrain_ge(mmp);
+ }
+ (Tilde, &MMP(major, minor, _)) => {
+ vr.constrain_lt(MM(major, minor + 1));
+ vr.constrain_ge(mmp);
+ }
+
+ (Caret, &MMP(0, 0, _)) => {
+ vr.constrain_lt(mmp.inclast());
+ vr.constrain_ge(mmp);
+ }
+ (Caret, &MMP(0, minor, _)) | (Caret, &MM(0, minor)) => {
+ vr.constrain_lt(MM(0, minor + 1));
+ vr.constrain_ge(mmp);
+ }
+ (Caret, &MMP(major, _, _)) | (Caret, &MM(major, _)) | (Caret, &M(major)) => {
+ vr.constrain_lt(M(major + 1));
+ vr.constrain_ge(mmp);
+ }
+
+ (_, _) => {
+ // https://github.com/dtolnay/semver/issues/262
+ panic!("Op is non-exhuastive for some reason");
+ }
+ }
+
+ Ok(())
+}
+
+/// Translates a Cargo dependency into a Debian package dependency.
+pub fn deb_dep(config: &Config, dep: &Dependency) -> Result<Vec<String>> // result is a AND-clause
+{
+ let dep_dashed = base_deb_name(&dep.package_name());
+ let mut suffixes = Vec::new();
+ if dep.uses_default_features() {
+ suffixes.push("+default-dev".to_string());
+ }
+ for feature in dep.features() {
+ suffixes.push(format!("+{}-dev", base_deb_name(feature)));
+ }
+ if suffixes.is_empty() {
+ suffixes.push("-dev".to_string());
+ }
+ let req = semver::VersionReq::parse(&dep.version_req().to_string()).unwrap();
+ let mut deps = Vec::new();
+ for suffix in suffixes {
+ let base = format!("{}-{}", Package::pkg_prefix(), dep_dashed);
+ let mut vr = VRange::new();
+ for p in &req.comparators {
+ let op = coerce_unacceptable_predicate(dep, p, config.allow_prerelease_deps)?;
+ generate_version_constraints(&mut vr, dep, p, op)?;
+ }
+ deps.push(vr.to_deb_or_clause(&base, &suffix)?);
+ }
+ Ok(deps)
+}
+
+pub fn deb_deps(config: &Config, cdeps: &[Dependency]) -> Result<Vec<String>> // result is a AND-clause
+{
+ let mut deps = Vec::new();
+ for dep in cdeps {
+ deps.extend(deb_dep(config, dep)?.iter().map(String::to_string));
+ }
+ deps.sort();
+ deps.dedup();
+ Ok(deps)
+}
+
+pub fn deb_dep_add_nocheck(x: &str) -> String {
+ x.to_string()
+ .split('|')
+ .map(|x| x.trim_end().to_string() + " <!nocheck> ")
+ .join("|")
+ .trim_end()
+ .to_string()
+}
--- /dev/null
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
--- /dev/null
+Debian systems provide the Apache 2.0 license in
+/usr/share/common-licenses/Apache-2.0
--- /dev/null
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+Debian systems provide the CC0 1.0 Universal License in
+/usr/share/common-licenses/CC0-1.0
--- /dev/null
+Debian systems provide the GPL 2.0 in /usr/share/common-licenses/GPL-2
--- /dev/null
+Debian systems provide the GPL 3.0 in /usr/share/common-licenses/GPL-3
--- /dev/null
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+Debian systems provide the LGPL 2.0 in /usr/share/common-licenses/LGPL-2
--- /dev/null
+Debian systems provide the LGPL 2.1 in /usr/share/common-licenses/LGPL-2.1
--- /dev/null
+Debian systems provide the LGPL 3.0 in /usr/share/common-licenses/LGPL-3
--- /dev/null
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+Distributions of all or part of the Software intended to be used
+by the recipients as they would use the unmodified Software,
+containing modifications that substantially alter, remove, or
+disable functionality of the Software, outside of the documented
+configuration mechanisms provided by the Software, shall be
+modified such that the Original Author's bug reporting email
+addresses and urls are either replaced with the contact information
+of the parties responsible for the changes, or removed entirely.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+Debian systems provide the MPL 1.1 in /usr/share/common-licenses/MPL-1.1
--- /dev/null
+Debian systems provide the MPL 2.0 in /usr/share/common-licenses/MPL-2.0
--- /dev/null
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
--- /dev/null
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::fs;
+use std::io::{self, ErrorKind, Read, Seek, Write as IoWrite};
+use std::os::unix::fs::PermissionsExt;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::str::FromStr;
+
+use anyhow::format_err;
+use chrono::{self, Datelike};
+use flate2::read::GzDecoder;
+use flate2::write::GzEncoder;
+use flate2::Compression;
+use regex::Regex;
+use tar::{Archive, Builder};
+use tempfile;
+
+use crate::config::{package_field_for_feature, testing_ignore_debpolv, Config, PackageKey};
+use crate::crates::{show_dep, transitive_deps, CrateDepInfo, CrateInfo};
+use crate::errors::*;
+use crate::util::{self, copy_tree, expect_success, get_transitive_val, traverse_depth};
+
+use self::changelog::{ChangelogEntry, ChangelogIterator};
+use self::control::{base_deb_name, deb_upstream_version};
+use self::control::{Description, Package, PkgTest, Source};
+use self::copyright::debian_copyright;
+pub use self::dependency::{deb_dep_add_nocheck, deb_deps};
+
+pub mod changelog;
+pub mod control;
+pub mod copyright;
+mod dependency;
+
+pub struct DebInfo {
+ upstream_name: String,
+ /// Debian package name without rust- prefix or any semver suffix
+ base_package_name: String,
+ /// Package name suffix after the base package name.
+ /// Some implies semver_suffix, i.e. Some("") is different from None
+ name_suffix: Option<String>,
+ uscan_version_pattern: Option<String>,
+ /// Debian package name without rust- prefix
+ package_name: String,
+ deb_upstream_version: String,
+ debcargo_version: String,
+ package_source_dir: PathBuf,
+ orig_tarball_path: PathBuf,
+}
+
+impl DebInfo {
+ pub fn new(crate_info: &CrateInfo, debcargo_version: &str, semver_suffix: bool) -> Self {
+ let upstream_name = crate_info.package_id().name().to_string();
+ let name_dashed = base_deb_name(&upstream_name);
+ let base_package_name = name_dashed.to_lowercase();
+ let (name_suffix, uscan_version_pattern, package_name) = if semver_suffix {
+ let semver = crate_info.semver();
+ let name_suffix = format!("-{}", &semver);
+ // See `man uscan` description of @ANY_VERSION@ on how these
+ // regex patterns were built.
+ let uscan = format!("[-_]?({}\\.\\d[\\-+\\.:\\~\\da-zA-Z]*)", &semver);
+ let pkgname = format!("{}{}", base_package_name, &name_suffix);
+ (Some(name_suffix), Some(uscan), pkgname)
+ } else {
+ (None, None, base_package_name.clone())
+ };
+ let deb_upstream_version = deb_upstream_version(crate_info.version());
+ let package_source_dir = PathBuf::from(format!(
+ "{}-{}-{}",
+ Source::pkg_prefix(),
+ package_name,
+ deb_upstream_version
+ ));
+ let orig_tarball_path = PathBuf::from(format!(
+ "{}-{}_{}.orig.tar.gz",
+ Source::pkg_prefix(),
+ package_name,
+ deb_upstream_version
+ ));
+
+ DebInfo {
+ upstream_name,
+ base_package_name,
+ name_suffix,
+ uscan_version_pattern,
+ package_name,
+ deb_upstream_version,
+ debcargo_version: debcargo_version.to_string(),
+ package_source_dir,
+ orig_tarball_path,
+ }
+ }
+
+ pub fn upstream_name(&self) -> &str {
+ self.upstream_name.as_str()
+ }
+
+ pub fn base_package_name(&self) -> &str {
+ self.base_package_name.as_str()
+ }
+
+ pub fn name_suffix(&self) -> Option<&str> {
+ self.name_suffix.as_deref()
+ }
+
+ pub fn package_name(&self) -> &str {
+ self.package_name.as_str()
+ }
+
+ pub fn deb_upstream_version(&self) -> &str {
+ self.deb_upstream_version.as_str()
+ }
+
+ pub fn debcargo_version(&self) -> &str {
+ self.debcargo_version.as_str()
+ }
+
+ pub fn package_source_dir(&self) -> &Path {
+ self.package_source_dir.as_ref()
+ }
+
+ pub fn orig_tarball_path(&self) -> &Path {
+ self.orig_tarball_path.as_ref()
+ }
+}
+
+pub fn prepare_orig_tarball(
+ crate_info: &CrateInfo,
+ tarball: &Path,
+ src_modified: bool,
+ output_dir: &Path,
+) -> Result<()> {
+ let crate_file = crate_info.crate_file();
+ let tempdir = tempfile::Builder::new()
+ .prefix("debcargo")
+ .tempdir_in(".")?;
+ let temp_archive_path = tempdir.path().join(tarball);
+
+ let mut create = fs::OpenOptions::new();
+ create.write(true).create_new(true);
+
+ if src_modified {
+ debcargo_info!("crate tarball was modified; repacking for debian");
+ let mut f = crate_file.file();
+ f.seek(io::SeekFrom::Start(0))?;
+ let mut archive = Archive::new(GzDecoder::new(f));
+ let mut new_archive = Builder::new(GzEncoder::new(
+ create.open(&temp_archive_path)?,
+ Compression::best(),
+ ));
+
+ for entry in archive.entries()? {
+ let entry = entry?;
+ let path = entry.path()?.into_owned();
+ if path.ends_with("Cargo.toml") && path.iter().count() == 2 {
+ // Put the rewritten and original Cargo.toml back into the orig tarball
+ let mut new_archive_append = |name: &str| {
+ let mut header = entry.header().clone();
+ let srcpath = output_dir.join(name);
+ header.set_path(path.parent().unwrap().join(name))?;
+ header.set_size(fs::metadata(&srcpath)?.len());
+ header.set_cksum();
+ new_archive.append(&header, fs::File::open(&srcpath)?)
+ };
+ new_archive_append("Cargo.toml")?;
+ new_archive_append("Cargo.toml.orig")?;
+ } else {
+ match crate_info.filter_path(&entry.path()?) {
+ Err(e) => debcargo_bail!(e),
+ Ok(r) => {
+ if !r {
+ new_archive.append(&entry.header().clone(), entry)?;
+ } else {
+ writeln!(
+ io::stderr(),
+ "Filtered out files from .orig.tar.gz: {:?}",
+ &entry.path()?
+ )?;
+ }
+ }
+ }
+ }
+ }
+
+ new_archive.finish()?;
+ } else {
+ fs::copy(crate_file.path(), &temp_archive_path)?;
+ }
+
+ fs::rename(temp_archive_path, &tarball)?;
+ Ok(())
+}
+
+pub fn apply_overlay_and_patches(
+ crate_info: &mut CrateInfo,
+ config_path: Option<&Path>,
+ config: &Config,
+ output_dir: &Path,
+) -> Result<tempfile::TempDir> {
+ let tempdir = tempfile::Builder::new()
+ .prefix("debcargo")
+ .tempdir_in(".")?;
+ let overlay = config.overlay_dir(config_path);
+ if let Some(p) = overlay.as_ref() {
+ for anc in tempdir.path().ancestors() {
+ if p.as_path() == anc {
+ debcargo_bail!(
+ "Aborting: refusing to copy an ancestor {} into a descendant {}",
+ p.as_path().display(),
+ tempdir.path().display(),
+ );
+ }
+ }
+ copy_tree(p.as_path(), tempdir.path()).unwrap();
+ }
+ if tempdir.path().join("control").exists() {
+ debcargo_warn!(
+ "Most of the time you shouldn't overlay debian/control, \
+it's a maintenance burden. Use debcargo.toml instead."
+ )
+ }
+ if tempdir.path().join("patches").join("series").exists() {
+ // apply patches to Cargo.toml in case they exist, and re-read it
+ let output_dir = &fs::canonicalize(&output_dir)?;
+ let stderr = || {
+ // create a new owned handle to stderr
+ fs::OpenOptions::new()
+ .append(true)
+ .open("/dev/stderr")
+ .unwrap()
+ };
+ expect_success(
+ Command::new("quilt")
+ .stdout(stderr())
+ .current_dir(&output_dir)
+ .env("QUILT_PATCHES", tempdir.path().join("patches"))
+ .args(&["push", "--quiltrc=-", "-a"]),
+ "failed to apply patches using quilt",
+ );
+ crate_info.replace_manifest(&output_dir.join("Cargo.toml"))?;
+ expect_success(
+ Command::new("quilt")
+ .stdout(stderr())
+ .current_dir(&output_dir)
+ .env("QUILT_PATCHES", tempdir.path().join("patches"))
+ .args(&["pop", "--quiltrc=-", "-a"]),
+ "failed to unapply patches",
+ );
+ }
+ Ok(tempdir)
+}
+
+#[allow(clippy::too_many_arguments)]
+pub fn prepare_debian_folder(
+ crate_info: &mut CrateInfo,
+ deb_info: &DebInfo,
+ config_path: Option<&Path>,
+ config: &Config,
+ output_dir: &Path,
+ tempdir: &tempfile::TempDir,
+ changelog_ready: bool,
+ copyright_guess_harder: bool,
+ overlay_write_back: bool,
+) -> Result<()> {
+ let mut create = fs::OpenOptions::new();
+ create.write(true).create_new(true);
+
+ let crate_name = crate_info.package_id().name();
+ let crate_version = crate_info.package_id().version();
+ let upstream_name = deb_info.upstream_name();
+
+ let maintainer = config.maintainer();
+ let uploaders: Vec<&str> = config
+ .uploaders()
+ .into_iter()
+ .flatten()
+ .map(String::as_str)
+ .collect();
+
+ let mut new_hints = vec![];
+ let mut file = |name: &str| {
+ let path = tempdir.path();
+ let f = path.join(name);
+ fs::create_dir_all(f.parent().unwrap())?;
+ create.open(&f).or_else(|e| match e.kind() {
+ ErrorKind::AlreadyExists => {
+ let hintname = name.to_owned() + util::HINT_SUFFIX;
+ let hint = path.join(&hintname);
+ if hint.exists() {
+ fs::remove_file(&hint)?;
+ }
+ new_hints.push(hintname);
+ create.open(&hint)
+ }
+ _ => Err(e),
+ })
+ };
+
+ // debian/cargo-checksum.json
+ {
+ let checksum = crate_info
+ .checksum()
+ .unwrap_or("Could not get crate checksum");
+ let mut cargo_checksum_json = file("cargo-checksum.json")?;
+ writeln!(
+ cargo_checksum_json,
+ r#"{{"package":"{}","files":{{}}}}"#,
+ checksum
+ )?;
+ }
+
+ // debian/compat
+ {
+ let mut compat = file("compat")?;
+ writeln!(compat, "12")?;
+ }
+
+ // debian/copyright
+ {
+ let mut copyright = io::BufWriter::new(file("copyright")?);
+ let year_range = if changelog_ready {
+ // if changelog is ready, unconditionally read the year range from it
+ changelog_first_last(tempdir.path())?
+ } else {
+ // otherwise use the first date if it exists
+ let last = chrono::Local::now().year();
+ match changelog_first_last(tempdir.path()) {
+ Ok((first, _)) => (first, last),
+ Err(_) => (last, last),
+ }
+ };
+ let dep5_copyright = debian_copyright(
+ output_dir,
+ crate_info.manifest(),
+ crate_info.manifest_path(),
+ maintainer,
+ &uploaders,
+ year_range,
+ copyright_guess_harder,
+ )?;
+ write!(copyright, "{}", dep5_copyright)?;
+ }
+
+ // debian/watch
+ {
+ let mut watch = file("watch")?;
+ match config.crate_src_path(config_path) {
+ Some(_) => write!(watch, "FIXME add uscan directive for local crate")?,
+ None => {
+ let uscan_version_pattern = deb_info
+ .uscan_version_pattern
+ .as_ref()
+ .map_or_else(|| "@ANY_VERSION@".to_string(), |ref s| s.to_string());
+ writeln!(watch, "version=4")?;
+ writeln!(
+ watch,
+ r"opts=filenamemangle=s/.*\/(.*)\/download/{name}-$1\.tar\.gz/g,\",
+ name = upstream_name
+ )?;
+ writeln!(
+ watch,
+ r"uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \"
+ )?;
+ writeln!(
+ watch,
+ "https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/{name} \
+ .*/crates/{name}/{version_pattern}/download",
+ name = upstream_name,
+ version_pattern = uscan_version_pattern
+ )?;
+ }
+ };
+ }
+
+ // debian/source/format
+ {
+ fs::create_dir_all(tempdir.path().join("source"))?;
+ let mut source_format = file("source/format")?;
+ writeln!(source_format, "3.0 (quilt)")?;
+ }
+
+ // debian/control & debian/tests/control
+ let (source, has_dev_depends, default_test_broken) =
+ prepare_debian_control(deb_info, crate_info, config, &mut file)?;
+
+ // for testing only, debian/debcargo_testing_bin/env
+ if testing_ignore_debpolv() {
+ fs::create_dir_all(tempdir.path().join("debcargo_testing_bin"))?;
+ let mut env_hack = file("debcargo_testing_bin/env")?;
+ env_hack.set_permissions(fs::Permissions::from_mode(0o777))?;
+ // intercept calls to dh-cargo-built-using
+ writeln!(
+ env_hack,
+ r#"#!/bin/sh
+case "$*" in */usr/share/cargo/bin/dh-cargo-built-using*)
+echo "debcargo testing: suppressing dh-cargo-built-using";;
+*) /usr/bin/env "$@";; esac
+"#
+ )?;
+ }
+
+ // debian/rules
+ {
+ let mut rules = file("rules")?;
+ rules.set_permissions(fs::Permissions::from_mode(0o777))?;
+ if has_dev_depends || testing_ignore_debpolv() {
+ // don't run any tests, we don't want extra B-D on dev-depends
+ // this could potentially cause B-D cycles so we avoid it
+ //
+ // also don't run crate tests during integration testing since some
+ // of them are brittle and fail; the purpose is to test debcargo
+ // not the actual crates
+ write!(
+ rules,
+ "{}",
+ concat!(
+ "#!/usr/bin/make -f\n",
+ "%:\n",
+ "\tdh $@ --buildsystem cargo\n"
+ )
+ )?;
+ // some crates need nightly to compile, annoyingly. only do this in
+ // testing; outside of testing the user should explicitly override
+ // debian/rules to do this
+ if testing_ignore_debpolv() {
+ writeln!(rules, "export RUSTC_BOOTSTRAP := 1")?;
+ writeln!(
+ rules,
+ "export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)"
+ )?;
+ }
+ } else {
+ write!(
+ rules,
+ "{}{}",
+ concat!(
+ "#!/usr/bin/make -f\n",
+ "%:\n",
+ "\tdh $@ --buildsystem cargo\n",
+ "\n",
+ "override_dh_auto_test:\n",
+ ),
+ // TODO: this logic is slightly brittle if another feature
+ // "provides" the default feature. In this case, you need to
+ // set test_is_broken explicitly on package."lib+default" and
+ // not package."lib+theotherfeature".
+ if default_test_broken {
+ "\tdh_auto_test -- test --all || true\n"
+ } else {
+ "\tdh_auto_test -- test --all\n"
+ },
+ )?;
+ }
+ }
+
+ // debian/changelog
+ if !changelog_ready {
+ let author = control::get_deb_author()?;
+ let crate_src = match config.crate_src_path(config_path) {
+ Some(_) => "local source",
+ None => "crates.io",
+ };
+ let autogenerated_item = format!(
+ " * Package {} {} from {} using debcargo {}",
+ &crate_name,
+ &crate_version,
+ &crate_src,
+ deb_info.debcargo_version()
+ );
+ let autogenerated_re = Regex::new(&format!(
+ r"^ \* Package (.*) (.*) from {} using debcargo (.*)$",
+ &crate_src
+ ))
+ .unwrap();
+
+ // Special-case d/changelog:
+ let (mut changelog, changelog_data) = changelog_or_new(tempdir.path())?;
+ let (changelog_old, mut changelog_items, deb_version_suffix) = {
+ let ver_bump = &|e: &Option<&str>| -> Result<Option<String>> {
+ Ok(match e {
+ Some(x) => {
+ let e = ChangelogEntry::from_str(x)?;
+ if e.version_parts().0 == deb_info.deb_upstream_version() {
+ Some(e.deb_version_suffix_bump())
+ } else {
+ None
+ }
+ }
+ None => None,
+ })
+ };
+ let mut chit = ChangelogIterator::from(&changelog_data);
+ let e1 = chit.next();
+ match e1 {
+ // If the first entry has changelog::DEFAULT_DIST then write over it smartly
+ Some(x) if x.contains(changelog::DEFAULT_DIST) => {
+ let mut e = ChangelogEntry::from_str(x)?;
+ if author == e.maintainer {
+ if let Some(pos) = e.items.iter().position(|x| autogenerated_re.is_match(x))
+ {
+ e.items[pos] = autogenerated_item;
+ } else {
+ e.items.push(autogenerated_item);
+ }
+ } else {
+ // If unreleased changelog is by someone else, preserve their entries
+ e.items.insert(0, autogenerated_item);
+ e.items.insert(1, "".to_string());
+ let ename = e.maintainer_name();
+ e.items.insert(2, format!(" [ {} ]", ename));
+ }
+ (&changelog_data[x.len()..], e.items, ver_bump(&chit.next())?)
+ }
+ // Otherwise prepend a new entry to the existing entries
+ _ => (
+ changelog_data.as_str(),
+ vec![autogenerated_item],
+ ver_bump(&e1)?,
+ ),
+ }
+ };
+
+ let source_deb_version = format!(
+ "{}-{}",
+ deb_info.deb_upstream_version(),
+ &deb_version_suffix.unwrap_or_else(|| "1".to_string())
+ );
+ if !uploaders.contains(&author.as_str()) {
+ debcargo_warn!(
+ "You ({}) are not in Uploaders; adding \"Team upload\" to d/changelog",
+ author
+ );
+ if !changelog_items.contains(&changelog::COMMENT_TEAM_UPLOAD.to_string()) {
+ changelog_items.insert(0, changelog::COMMENT_TEAM_UPLOAD.to_string());
+ }
+ }
+ let changelog_new_entry = ChangelogEntry::new(
+ source.name().to_string(),
+ source_deb_version,
+ changelog::DEFAULT_DIST.to_string(),
+ "urgency=medium".to_string(),
+ author,
+ changelog::local_now(),
+ changelog_items,
+ );
+
+ changelog.seek(io::SeekFrom::Start(0))?;
+ if changelog_old.is_empty() {
+ write!(changelog, "{}", changelog_new_entry)?;
+ } else {
+ write!(changelog, "{}\n{}", changelog_new_entry, changelog_old)?;
+ }
+ // the new file might be shorter, truncate it to the current cursor position
+ let pos = changelog.seek(io::SeekFrom::Current(0))?;
+ changelog.set_len(pos)?;
+ }
+
+ if overlay_write_back {
+ let overlay = config.overlay_dir(config_path);
+ if let Some(p) = overlay.as_ref() {
+ if !changelog_ready {
+ // Special-case d/changelog:
+ // Always write it back, this is safe because of our prepending logic
+ new_hints.push("changelog".to_string());
+ }
+ for hint in &new_hints {
+ let newpath = tempdir.path().join(hint);
+ let oldpath = p.join(hint);
+ fs::copy(newpath, oldpath).expect("could not write back");
+ debcargo_info!("Wrote back file to overlay: {}", hint);
+ }
+ }
+ }
+
+ fs::rename(tempdir.path(), output_dir.join("debian"))?;
+ Ok(())
+}
+
+fn prepare_debian_control<F: FnMut(&str) -> std::result::Result<std::fs::File, std::io::Error>>(
+ deb_info: &DebInfo,
+ crate_info: &CrateInfo,
+ config: &Config,
+ mut file: F,
+) -> Result<(Source, bool, bool)> {
+ let crate_name = crate_info.crate_name();
+ let deb_upstream_version = deb_info.deb_upstream_version();
+ let base_pkgname = deb_info.base_package_name();
+ let name_suffix = deb_info.name_suffix();
+
+ let lib = crate_info.is_lib();
+ let mut bins = crate_info.get_binary_targets();
+ if lib && !bins.is_empty() && !config.build_bin_package() {
+ bins.clear();
+ }
+ let bin_name = if config.bin_name.eq(&Config::default().bin_name) {
+ let default_bin_name = deb_info.base_package_name();
+ if !bins.is_empty() {
+ debcargo_info!(
+ "Generate binary crate with default name '{}', set bin_name to override or bin = false to disable.",
+ &default_bin_name
+ );
+ }
+ default_bin_name
+ } else {
+ config.bin_name.as_str()
+ };
+
+ let maintainer = config.maintainer();
+ let requires_root = config.requires_root();
+ let uploaders: Vec<&str> = config
+ .uploaders()
+ .into_iter()
+ .flatten()
+ .map(String::as_str)
+ .collect();
+
+ let features_with_deps = crate_info.all_dependencies_and_features();
+ let dev_depends = deb_deps(config, &crate_info.dev_dependencies())?;
+ log::trace!(
+ "features_with_deps: {:?}",
+ features_with_deps
+ .iter()
+ .map(|(&f, &(ref ff, ref dd))| {
+ (f, (ff, dd.iter().map(show_dep).collect::<Vec<_>>()))
+ })
+ .collect::<Vec<_>>()
+ );
+ let meta = crate_info.metadata();
+
+ // debian/tests/control, preparation
+ let test_is_marked_broken = |f: &str| config.package_test_is_broken(PackageKey::feature(f));
+ let test_is_broken = |f: &str| {
+ let getparents = |f: &str| features_with_deps.get(f).map(|(d, _)| d);
+ match get_transitive_val(&getparents, &test_is_marked_broken, f) {
+ Err((k, vv)) => debcargo_bail!(
+ "{} {}: {}: {:?}",
+ "error trying to recursively determine test_is_broken for",
+ k,
+ "dependencies have inconsistent config values",
+ vv
+ ),
+ Ok(v) => Ok(v.unwrap_or(false)),
+ }
+ };
+
+ let build_deps = {
+ let build_deps = ["debhelper (>= 12)", "dh-cargo (>= 25)"]
+ .iter()
+ .map(|x| x.to_string());
+ // note: please keep this in sync with build_order::dep_features
+ let (default_features, default_deps) = transitive_deps(&features_with_deps, "default");
+ //debcargo_info!("default_features: {:?}", default_features);
+ //debcargo_info!("default_deps: {:?}", deb_deps(config, &default_deps)?);
+ let extra_override_deps = package_field_for_feature(
+ &|x| config.package_depends(x),
+ PackageKey::feature("default"),
+ &default_features,
+ );
+ let build_deps_extra = ["cargo:native", "rustc:native", "libstd-rust-dev"]
+ .iter()
+ .map(|s| s.to_string())
+ .chain(deb_deps(config, &default_deps)?)
+ .chain(extra_override_deps);
+ if !bins.is_empty() {
+ build_deps.chain(build_deps_extra).collect()
+ } else {
+ assert!(lib);
+ build_deps
+ .chain(build_deps_extra.map(|d| deb_dep_add_nocheck(&d)))
+ .collect()
+ }
+ };
+ let mut source = Source::new(
+ base_pkgname,
+ name_suffix,
+ crate_name,
+ if let Some(ref home) = meta.homepage {
+ home
+ } else {
+ ""
+ },
+ lib,
+ maintainer.to_string(),
+ uploaders.iter().map(|s| s.to_string()).collect(),
+ build_deps,
+ if requires_root.is_some() {
+ requires_root.as_ref().unwrap().to_string()
+ } else {
+ "no".to_string()
+ },
+ )?;
+
+ // If source overrides are present update related parts.
+ source.apply_overrides(config);
+
+ let mut control = io::BufWriter::new(file("control")?);
+ write!(control, "{}", source)?;
+
+ // Summary and description generated from Cargo.toml
+ let (crate_summary, crate_description) = crate_info.get_summary_description();
+ let summary_prefix = crate_summary.unwrap_or(format!("Rust crate \"{}\"", crate_name));
+ let description_prefix = {
+ let tmp = crate_description.unwrap_or_else(|| "".to_string());
+ if tmp.is_empty() {
+ tmp
+ } else {
+ format!("{}\n.\n", tmp)
+ }
+ };
+
+ if lib {
+ // debian/tests/control
+ let all_features_test_broken = Some(&"@")
+ .into_iter()
+ .chain(features_with_deps.keys())
+ .any(|f| test_is_marked_broken(f).unwrap_or(false));
+ let all_features_test_depends = Some(&"@")
+ .into_iter()
+ .chain(features_with_deps.keys())
+ .map(|f| {
+ config
+ .package_test_depends(PackageKey::feature(f))
+ .into_iter()
+ .flatten()
+ })
+ .flatten()
+ .map(|s| s.to_string())
+ .chain(dev_depends.clone())
+ .collect::<Vec<_>>();
+ let mut testctl = io::BufWriter::new(file("tests/control")?);
+ write!(
+ testctl,
+ "{}",
+ PkgTest::new(
+ source.name(),
+ crate_name,
+ "@",
+ deb_upstream_version,
+ vec!["--all-features"],
+ &all_features_test_depends,
+ if all_features_test_broken {
+ vec!["flaky"]
+ } else {
+ vec![]
+ },
+ )?
+ )?;
+
+ // begin transforming dependencies
+ let working_features_with_deps = features_with_deps.clone();
+ let working_features_with_deps = {
+ let mut working_features_with_deps = working_features_with_deps;
+ // Detect corner case with feature naming regarding _ vs -.
+ // Debian does not support _ in package names. Cargo automatically
+ // converts - in crate names to _, but features (including optional
+ // dependencies) can have both _ and -.
+ let potential_corner_case = working_features_with_deps
+ .keys()
+ .filter(|x| base_deb_name(x).as_str() != **x)
+ .cloned()
+ .collect::<Vec<_>>();
+ for f in potential_corner_case {
+ let f_ = base_deb_name(f);
+ if let Some((df1, dd1)) = working_features_with_deps.remove(f_.as_str()) {
+ // merge dependencies of f_ and f
+ working_features_with_deps
+ .entry(f)
+ .and_modify(|(df0, dd0)| {
+ let mut df = BTreeSet::from_iter(df0.drain(..));
+ df.extend(df1);
+ df.remove(f_.as_str());
+ df.remove(f);
+ let mut dd: HashSet<cargo::core::Dependency> =
+ HashSet::from_iter(dd0.drain(..));
+ dd.extend(dd1);
+ df0.extend(df);
+ dd0.extend(dd);
+ });
+ // go through other feature deps and change f_ to f
+ for (_, (df, _)) in working_features_with_deps.iter_mut() {
+ for feat in df.iter_mut() {
+ if *feat == f_.as_str() {
+ *feat = f;
+ }
+ }
+ }
+ // check we didn't create a cycle in features
+ let dep_feats = traverse_depth(
+ &|k: &&'static str| working_features_with_deps.get(k).map(|x| &x.0),
+ f,
+ );
+ if dep_feats.contains(f) {
+ log::debug!("transitive deps of feature {}: {:?}", f, dep_feats);
+ debcargo_bail!(
+ "Tried to merge features {} and {} as they are not representable separately\n\
+ in Debian, but this resulted in a feature cycle. You need to manually patch the package.", f, f_);
+ } else {
+ debcargo_warn!(
+ "Merged features {} and {} as they are not representable separately in Debian.\n\
+ We checked that this does not break the package in an obvious way (feature cycle), however\n\
+ if there is a more sophisticated breakage, you'll have to manually patch those \
+ features instead.", f, f_);
+ }
+ }
+ }
+ working_features_with_deps
+ };
+ log::trace!(
+ "working_features_with_deps: {:?}",
+ working_features_with_deps
+ .iter()
+ .map(|(&f, &(ref ff, ref dd))| {
+ (f, (ff, dd.iter().map(show_dep).collect::<Vec<_>>()))
+ })
+ .collect::<Vec<_>>()
+ );
+ let (mut provides, reduced_features_with_deps) = if config.collapse_features {
+ debcargo_warn!(
+ "You are using the collapse_features work-around, which makes the resulting"
+ );
+ debcargo_warn!(
+ "package uninstallable when (now or in the future) your crate dependencies"
+ );
+ debcargo_warn!(
+ "contain cyclic dependencies on the crate-level; this is because cargo only"
+ );
+ debcargo_warn!("enforces acyclicity of dependencies on the per-feature level.");
+ debcargo_warn!("");
+ debcargo_warn!(
+ "By switching on collapse_features, you are telling debcargo to generate Debian"
+ );
+ debcargo_warn!(
+ "binary package on a per-crate-level basis and not a per-feature-level, meaning"
+ );
+ debcargo_warn!(
+ "that there is the chance of generating a dependency cycle on the Debian binary"
+ );
+ debcargo_warn!("package level, which APT by default refuses to install.");
+ debcargo_warn!("");
+ debcargo_warn!(
+ "You should not be doing this just because \"somebody told you so\"; you should"
+ );
+ debcargo_warn!(
+ "understand the situation and be prepared to deal with future technical debt"
+ );
+ debcargo_warn!("when the aforementioned cycles arise.");
+ debcargo_warn!("");
+ debcargo_warn!(
+ "Note that a long-term solution has been discussed with the FTP team and is in"
+ );
+ debcargo_warn!(
+ "progress - namely to move Debian rust packages into a separate section of the"
+ );
+ debcargo_warn!(
+ "archive, which will then have the stricter current NEW rules lifted, and then"
+ );
+ debcargo_warn!("the collapse_features work around would no longer be necessary.");
+ debcargo_warn!("");
+ debcargo_warn!("A basic example of the above would be:");
+ debcargo_warn!("");
+ debcargo_warn!("- crate A with feature AX depends on crate B with feature BY");
+ debcargo_warn!("- crate B with feature BX depends on crate A with feature AY");
+ debcargo_warn!("");
+ debcargo_warn!(
+ "This is a perfectly valid situation in the rust+cargo ecosystem. Notice that"
+ );
+ debcargo_warn!(
+ "there is no dependency cycle on the per-feature level, and this is enforced by"
+ );
+ debcargo_warn!(
+ "cargo; but if collapse_features is used then package A+AX+AY would cyclicly"
+ );
+ debcargo_warn!("depend on package B+BX+BY.");
+ collapse_features(working_features_with_deps)
+ } else {
+ reduce_provides(working_features_with_deps)
+ };
+ log::trace!(
+ "reduced_features_with_deps: {:?}",
+ reduced_features_with_deps
+ .iter()
+ .map(|(&f, &(ref ff, ref dd))| {
+ (f, (ff, dd.iter().map(show_dep).collect::<Vec<_>>()))
+ })
+ .collect::<Vec<_>>()
+ );
+ // end transforming dependencies
+
+ log::trace!("provides: {:?}", provides);
+ let mut recommends = vec![];
+ let mut suggests = vec![];
+ for (&feature, features) in provides.iter() {
+ if feature.is_empty() {
+ continue;
+ } else if feature == "default" || features.contains(&"default") {
+ recommends.push(feature);
+ } else {
+ suggests.push(feature);
+ }
+ }
+
+ for (feature, (f_deps, o_deps)) in reduced_features_with_deps.into_iter() {
+ let pk = PackageKey::feature(feature);
+ let f_provides = provides.remove(feature).unwrap();
+ let mut crate_features = f_provides.clone();
+ crate_features.push(feature);
+
+ let summary_suffix = if feature.is_empty() {
+ " - Rust source code".to_string()
+ } else {
+ match f_provides.len() {
+ 0 => format!(" - feature \"{}\"", feature),
+ _ => format!(" - feature \"{}\" and {} more", feature, f_provides.len()),
+ }
+ };
+ let description_suffix = if feature.is_empty() {
+ format!(
+ "This package contains the source for the \
+ Rust {} crate, packaged by debcargo for use \
+ with cargo and dh-cargo.",
+ crate_name
+ )
+ } else {
+ format!(
+ "This metapackage enables feature \"{}\" for the \
+ Rust {} crate, by pulling in any additional \
+ dependencies needed by that feature.{}",
+ feature,
+ crate_name,
+ match f_provides.len() {
+ 0 => "".to_string(),
+ 1 => format!(
+ "\n\nAdditionally, this package also provides the \
+ \"{}\" feature.",
+ f_provides[0],
+ ),
+ _ => format!(
+ "\n\nAdditionally, this package also provides the \
+ \"{}\", and \"{}\" features.",
+ f_provides[..f_provides.len() - 1].join("\", \""),
+ f_provides[f_provides.len() - 1],
+ ),
+ },
+ )
+ };
+ let mut package = Package::new(
+ base_pkgname,
+ name_suffix,
+ crate_info.version(),
+ Description {
+ prefix: summary_prefix.clone(),
+ suffix: summary_suffix.clone(),
+ },
+ Description {
+ prefix: description_prefix.clone(),
+ suffix: description_suffix.clone(),
+ },
+ if feature.is_empty() {
+ None
+ } else {
+ Some(feature)
+ },
+ f_deps,
+ deb_deps(config, &o_deps)?,
+ f_provides.clone(),
+ if feature.is_empty() {
+ recommends.clone()
+ } else {
+ vec![]
+ },
+ if feature.is_empty() {
+ suggests.clone()
+ } else {
+ vec![]
+ },
+ )?;
+ // If any overrides present for this package it will be taken care.
+ package.apply_overrides(config, pk, f_provides);
+
+ match package.summary_check_len() {
+ Err(()) => writeln!(
+ control,
+ concat!(
+ "\n",
+ "# FIXME (packages.\"(name)\".section) debcargo ",
+ "auto-generated summary for {} is very long, consider overriding"
+ ),
+ package.name(),
+ )?,
+ Ok(()) => {}
+ };
+
+ write!(control, "\n{}", package)?;
+
+ // Override pointless overzealous warnings from lintian
+ if !feature.is_empty() {
+ let mut overrides =
+ io::BufWriter::new(file(&format!("{}.lintian-overrides", package.name()))?);
+ write!(
+ overrides,
+ "{} binary: empty-rust-library-declares-provides *",
+ package.name()
+ )?;
+ }
+
+ // Generate tests for all features in this package
+ for f in crate_features {
+ let (feature_deps, _) = transitive_deps(&features_with_deps, f);
+
+ // args
+ let mut args = if f == "default" || feature_deps.contains(&"default") {
+ vec![]
+ } else {
+ vec!["--no-default-features"]
+ };
+ // --features default sometimes fails, see
+ // https://github.com/rust-lang/cargo/issues/8164
+ if !f.is_empty() && f != "default" {
+ args.push("--features");
+ args.push(f);
+ }
+
+ // deps
+ let test_depends = Some(f)
+ .into_iter()
+ .chain(feature_deps)
+ .map(|f| {
+ config
+ .package_test_depends(PackageKey::feature(f))
+ .into_iter()
+ .flatten()
+ })
+ .flatten()
+ .map(|s| s.to_string())
+ .chain(dev_depends.clone())
+ .collect::<Vec<_>>();
+ let pkgtest = PkgTest::new(
+ package.name(),
+ crate_name,
+ f,
+ deb_upstream_version,
+ args,
+ &test_depends,
+ if test_is_broken(f)? {
+ vec!["flaky"]
+ } else {
+ vec![]
+ },
+ )?;
+ write!(testctl, "\n{}", pkgtest)?;
+ }
+ }
+ assert!(provides.is_empty());
+ // reduced_features_with_deps consumed by into_iter, no longer usable
+ }
+
+ if !bins.is_empty() {
+ // adding " - binaries" is a bit redundant for users, so just leave as-is
+ let summary_suffix = "".to_string();
+ let description_suffix = format!(
+ "This package contains the following binaries built from the Rust crate\n\"{}\":\n - {}",
+ crate_name,
+ bins.join("\n - ")
+ );
+
+ let mut bin_pkg = Package::new_bin(
+ bin_name,
+ name_suffix,
+ // if not-a-lib then Source section is already FIXME
+ if !lib {
+ None
+ } else {
+ Some("FIXME-(packages.\"(name)\".section)")
+ },
+ Description {
+ prefix: summary_prefix,
+ suffix: summary_suffix,
+ },
+ Description {
+ prefix: description_prefix,
+ suffix: description_suffix,
+ },
+ );
+
+ // Binary package overrides.
+ bin_pkg.apply_overrides(config, PackageKey::Bin, vec![]);
+ write!(control, "\n{}", bin_pkg)?;
+ }
+
+ Ok((source, !dev_depends.is_empty(), test_is_broken("default")?))
+}
+
+fn collapse_features(
+ orig_features_with_deps: CrateDepInfo,
+) -> (BTreeMap<&'static str, Vec<&'static str>>, CrateDepInfo) {
+ let (provides, deps) = orig_features_with_deps.iter().fold(
+ (Vec::new(), Vec::new()),
+ |(mut provides, mut deps), (f, (_, f_deps))| {
+ if f != &"" {
+ provides.push(*f);
+ }
+ deps.append(&mut f_deps.clone());
+ (provides, deps)
+ },
+ );
+
+ let mut collapsed_provides = BTreeMap::new();
+ collapsed_provides.insert("", provides);
+
+ let mut collapsed_features_with_deps = BTreeMap::new();
+ collapsed_features_with_deps.insert("", (Vec::new(), deps));
+
+ (collapsed_provides, collapsed_features_with_deps)
+}
+
+/// Calculate Provides: in an attempt to reduce the number of binaries.
+///
+/// The algorithm is very simple and incomplete. e.g. it does not, yet
+/// simplify things like:
+/// f1 depends on f2, f3
+/// f2 depends on f4
+/// f3 depends on f4
+/// into
+/// f4 provides f1, f2, f3
+fn reduce_provides(
+ mut features_with_deps: CrateDepInfo,
+) -> (BTreeMap<&'static str, Vec<&'static str>>, CrateDepInfo) {
+ // If any features have duplicate dependencies, deduplicate them by
+ // making all of the subsequent ones depend on the first one.
+ let mut features_rev_deps = HashMap::new();
+ for (&f, dep) in features_with_deps.iter() {
+ if !features_rev_deps.contains_key(dep) {
+ features_rev_deps.insert(dep.clone(), vec![]);
+ }
+ features_rev_deps.get_mut(dep).unwrap().push(f);
+ }
+ for (_, ff) in features_rev_deps.into_iter() {
+ let f0 = ff[0];
+ for f in &ff[1..] {
+ features_with_deps.insert(f, (vec![f0], vec![]));
+ }
+ }
+
+ // Calculate provides by following 0- or 1-length dependency lists.
+ let mut provides = BTreeMap::new();
+ let mut provided = Vec::new();
+ for (&f, (ref ff, ref dd)) in features_with_deps.iter() {
+ //debcargo_info!("provides considering: {:?}", &f);
+ if !dd.is_empty() {
+ continue;
+ }
+ assert!(!ff.is_empty() || f.is_empty());
+ let k = if ff.len() == 1 {
+ // if A depends on a single feature B, then B provides A.
+ ff[0]
+ } else {
+ continue;
+ };
+ //debcargo_info!("provides still considering: {:?}", &f);
+ if !provides.contains_key(k) {
+ provides.insert(k, vec![]);
+ }
+ provides.get_mut(k).unwrap().push(f);
+ provided.push(f);
+ }
+
+ //debcargo_info!("provides-internal: {:?}", &provides);
+ //debcargo_info!("provided-internal: {:?}", &provided);
+ for p in provided {
+ features_with_deps.remove(p);
+ }
+
+ let provides = features_with_deps
+ .keys()
+ .map(|k| {
+ (
+ *k,
+ traverse_depth(&|k: &&'static str| provides.get(k), k)
+ .into_iter()
+ .collect::<Vec<_>>(),
+ )
+ })
+ .collect::<BTreeMap<_, _>>();
+
+ (provides, features_with_deps)
+}
+
+fn changelog_or_new(tempdir: &Path) -> Result<(fs::File, String)> {
+ let mut changelog = fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(tempdir.join("changelog"))?;
+ let mut changelog_data = String::new();
+ changelog.read_to_string(&mut changelog_data)?;
+ Ok((changelog, changelog_data))
+}
+
+fn changelog_first_last(tempdir: &Path) -> Result<(i32, i32)> {
+ let mut changelog = fs::File::open(tempdir.join("changelog"))?;
+ let mut changelog_data = String::new();
+ changelog.read_to_string(&mut changelog_data)?;
+ let mut last = None;
+ let mut first = None;
+ for x in ChangelogIterator::from(&changelog_data) {
+ let e = ChangelogEntry::from_str(x)?;
+ if None == last {
+ last = Some(e.date.year());
+ }
+ first = Some(e.date.year());
+ }
+ if None == last {
+ Err(format_err!("changelog had no entries"))
+ } else {
+ Ok((first.unwrap(), last.unwrap()))
+ }
+}
--- /dev/null
+use anyhow;
+
+pub type Result<T> = ::std::result::Result<T, anyhow::Error>;
+pub use anyhow::format_err;
+
+#[macro_export]
+macro_rules! debcargo_info {
+ ($e:expr) => {
+ {
+ use ansi_term::Colour::Green;
+ eprintln!("{}", Green.paint($e));
+ }
+ };
+
+ ($fmt:expr, $( $arg:tt)+) => {
+ {
+ use ansi_term::Colour::Green;
+ let print_string = format!($fmt, $($arg)+);
+ eprintln!("{}", Green.paint(print_string));
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! debcargo_warn {
+ ($e:expr) => {
+ {
+ use ansi_term::Colour::RGB;
+ eprintln!("{}", RGB(255,165,0).bold().paint($e));
+ }
+ };
+
+ ($fmt:expr, $( $arg:tt)+) => {
+ {
+ use ansi_term::Colour::RGB;
+ let print_string = RGB(255,165,0).bold().paint(format!($fmt, $($arg)+));
+ eprintln!("{}", print_string);
+ }
+ };
+
+}
+
+#[macro_export]
+macro_rules! debcargo_bail {
+ ($e:expr) => {{
+ return Err(::anyhow::format_err!("{}", $e));
+ }};
+
+ ($fmt:expr, $( $arg:tt)+) => {
+ {
+ let error_string = format!($fmt, $($arg)+);
+ return Err(::anyhow::format_err!("{}", error_string));
+ }
+ };
+}
--- /dev/null
+#[macro_use]
+pub mod errors;
+pub mod config;
+pub mod crates;
+pub mod debian;
+mod util;
+
+pub mod build_order;
+pub mod package;
--- /dev/null
+use std::path::PathBuf;
+
+use anyhow::Context;
+
+use clap::{crate_version, Parser};
+
+use crate::config::Config;
+use crate::crates::CrateInfo;
+use crate::debian::{self, DebInfo};
+use crate::errors::Result;
+use crate::util;
+
+pub struct PackageProcess {
+ // below state is filled in during init
+ pub crate_info: CrateInfo,
+ pub deb_info: DebInfo,
+ pub config_path: Option<PathBuf>,
+ pub config: Config,
+ // below state is filled in during the process
+ /// Output directory as specified by the user.
+ pub output_dir: Option<PathBuf>,
+ pub source_modified: Option<bool>,
+ /// Tempdir that contains a working copy of the eventual output.
+ pub temp_output_dir: Option<tempfile::TempDir>,
+ pub orig_tarball: Option<PathBuf>,
+}
+
+#[derive(Debug, Clone, Parser)]
+pub struct PackageInitArgs {
+ /// Name of the crate to package.
+ pub crate_name: String,
+ /// Version of the crate to package; may contain dependency operators.
+ /// If empty string or omitted, resolves to the latest version.
+ pub version: Option<String>,
+ /// TOML file providing package-specific options.
+ #[clap(long)]
+ pub config: Option<PathBuf>,
+}
+
+#[derive(Debug, Clone, Parser)]
+pub struct PackageExtractArgs {
+ /// Output directory for the package. The orig tarball is named according
+ /// to Debian conventions in the parent directory of this directory.
+ #[clap(long)]
+ pub directory: Option<PathBuf>,
+}
+
+#[derive(Debug, Clone, Parser)]
+pub struct PackageExecuteArgs {
+ /// Assume the changelog is already bumped, and leave it alone.
+ #[clap(long)]
+ pub changelog_ready: bool,
+ /// Guess extra values for d/copyright. Might be slow.
+ #[clap(long)]
+ pub copyright_guess_harder: bool,
+ /// Don't write back hint files or d/changelog to the source overlay directory.
+ #[clap(long)]
+ pub no_overlay_write_back: bool,
+}
+
+impl PackageProcess {
+ /// More fine-grained access. For normal usage see `Self::init` instead.
+ pub fn new(
+ mut crate_info: CrateInfo,
+ config_path: Option<PathBuf>,
+ config: Config,
+ ) -> Result<Self> {
+ crate_info.set_includes_excludes(config.orig_tar_excludes(), config.orig_tar_whitelist());
+ let deb_info = DebInfo::new(&crate_info, crate_version!(), config.semver_suffix);
+
+ Ok(Self {
+ crate_info,
+ deb_info,
+ config_path,
+ config,
+ output_dir: None,
+ source_modified: None,
+ temp_output_dir: None,
+ orig_tarball: None,
+ })
+ }
+
+ pub fn init(init_args: PackageInitArgs) -> Result<Self> {
+ let crate_name = &init_args.crate_name;
+ let version = init_args.version.as_deref();
+ let config = init_args.config;
+
+ let (config_path, config) = match config {
+ Some(path) => {
+ let config = Config::parse(&path).context("failed to parse debcargo.toml")?;
+ (Some(path), config)
+ }
+ None => (None, Config::default()),
+ };
+
+ let crate_path = config.crate_src_path(config_path.as_deref());
+ let crate_info = match crate_path {
+ Some(p) => CrateInfo::new_with_local_crate(crate_name, version, &p)?,
+ None => CrateInfo::new(crate_name, version)?,
+ };
+
+ Self::new(crate_info, config_path, config)
+ }
+
+ pub fn extract(&mut self, extract: PackageExtractArgs) -> Result<()> {
+ assert!(self.output_dir.is_none());
+ assert!(self.source_modified.is_none());
+ let Self {
+ crate_info,
+ deb_info,
+ ..
+ } = self;
+ // vars read; begin stage
+
+ let output_dir = extract
+ .directory
+ .unwrap_or_else(|| deb_info.package_source_dir().to_path_buf());
+
+ let source_modified = crate_info.extract_crate(&output_dir)?;
+
+ // stage finished; set vars
+ self.output_dir = Some(output_dir);
+ self.source_modified = Some(source_modified);
+ Ok(())
+ }
+
+ pub fn apply_overrides(&mut self) -> Result<()> {
+ assert!(self.temp_output_dir.is_none());
+ let Self {
+ crate_info,
+ config_path,
+ config,
+ output_dir,
+ ..
+ } = self;
+ let output_dir = output_dir.as_ref().unwrap();
+ // vars read; begin stage
+
+ let temp_output_dir = debian::apply_overlay_and_patches(
+ crate_info,
+ config_path.as_deref(),
+ config,
+ output_dir,
+ )?;
+
+ // stage finished; set vars
+ self.temp_output_dir = Some(temp_output_dir);
+ Ok(())
+ }
+
+ pub fn prepare_orig_tarball(&mut self) -> Result<()> {
+ assert!(self.orig_tarball.is_none());
+ let Self {
+ crate_info,
+ deb_info,
+ output_dir,
+ source_modified,
+ ..
+ } = self;
+ let output_dir = output_dir.as_ref().unwrap();
+ let source_modified = source_modified.as_ref().unwrap();
+ // vars read; begin stage
+
+ let orig_tarball = output_dir
+ .parent()
+ .unwrap()
+ .join(deb_info.orig_tarball_path());
+ debian::prepare_orig_tarball(crate_info, &orig_tarball, *source_modified, output_dir)?;
+
+ // stage finished; set vars
+ self.orig_tarball = Some(orig_tarball);
+ Ok(())
+ }
+
+ pub fn prepare_debian_folder(&mut self, args: PackageExecuteArgs) -> Result<()> {
+ let Self {
+ crate_info,
+ deb_info,
+ config_path,
+ config,
+ output_dir,
+ temp_output_dir,
+ ..
+ } = self;
+ let output_dir = output_dir.as_ref().unwrap();
+ let temp_output_dir = temp_output_dir.as_ref().unwrap();
+ // vars read; begin stage
+
+ debian::prepare_debian_folder(
+ crate_info,
+ deb_info,
+ config_path.as_deref(),
+ config,
+ output_dir,
+ temp_output_dir,
+ args.changelog_ready,
+ args.copyright_guess_harder,
+ !args.no_overlay_write_back,
+ )?;
+
+ // stage finished; set vars
+ Ok(())
+ }
+
+ pub fn post_package_checks(&self) -> Result<()> {
+ let Self {
+ config_path,
+ config,
+ output_dir,
+ orig_tarball,
+ ..
+ } = self;
+ let output_dir = output_dir.as_ref().unwrap();
+ let orig_tarball = orig_tarball.as_ref().unwrap();
+
+ let curdir = std::env::current_dir()?;
+ debcargo_info!(
+ concat!("Package Source: {}\n", "Original Tarball for package: {}\n"),
+ util::rel_p(output_dir, &curdir),
+ util::rel_p(orig_tarball, &curdir)
+ );
+ let fixmes = util::lookup_fixmes(output_dir.join("debian").as_path())?;
+ if !fixmes.is_empty() {
+ debcargo_warn!("FIXME found in the following files.");
+ for f in fixmes {
+ if util::hint_file_for(&f).is_some() {
+ debcargo_warn!("\t(•) {}", util::rel_p(&f, &curdir));
+ } else {
+ debcargo_warn!("\t • {}", util::rel_p(&f, &curdir));
+ }
+ }
+ debcargo_warn!("");
+ debcargo_warn!("To fix, try combinations of the following: ");
+ match config_path.as_deref() {
+ None => debcargo_warn!("\t • Write a config file and use it with --config"),
+ Some(c) => {
+ debcargo_warn!("\t • Add or edit overrides in your config file:");
+ debcargo_warn!("\t {}", util::rel_p(c, &curdir));
+ }
+ };
+ match config.overlay_dir(config_path.as_deref()) {
+ None => debcargo_warn!("\t • Create an overlay directory and add it to your config file with overlay = \"/path/to/overlay\""),
+ Some(p) => {
+ debcargo_warn!("\t • Add or edit files in your overlay directory:");
+ debcargo_warn!("\t {}", util::rel_p(&p, &curdir));
+ }
+ }
+ }
+ Ok(())
+ }
+}
--- /dev/null
+use std::collections::{BTreeMap, BTreeSet, VecDeque};
+use std::ffi::OsStr;
+use std::fmt;
+use std::fs;
+use std::io::{BufRead, BufReader, Error};
+use std::iter::Iterator;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::fs::symlink;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use itertools::Itertools;
+use walkdir::WalkDir;
+
+pub const HINT_SUFFIX: &str = ".debcargo.hint";
+
+pub fn hint_file_for(file: &Path) -> Option<&Path> {
+ let file = file.as_os_str().as_bytes();
+ if file.len() >= HINT_SUFFIX.len()
+ && &file[file.len() - HINT_SUFFIX.len()..] == HINT_SUFFIX.as_bytes()
+ {
+ Some(Path::new(OsStr::from_bytes(
+ &file[..file.len() - HINT_SUFFIX.len()],
+ )))
+ } else {
+ None
+ }
+}
+
+pub fn lookup_fixmes(srcdir: &Path) -> Result<BTreeSet<PathBuf>, Error> {
+ let mut fixmes = BTreeSet::new();
+ for entry in WalkDir::new(srcdir) {
+ let entry = entry?;
+ if entry.file_type().is_file() {
+ let file = fs::File::open(entry.path())?;
+ let reader = BufReader::new(file);
+ // If we find one FIXME we break the loop and check next file. Idea
+ // is only to find files with FIXME strings in it.
+ for line in reader.lines().flatten() {
+ if line.contains("FIXME") {
+ fixmes.insert(entry.path().to_path_buf());
+ break;
+ }
+ }
+ }
+ }
+
+ // ignore hint files whose non-hint partners exists and don't have a FIXME
+ let fixmes = fixmes
+ .iter()
+ .filter(|f| match hint_file_for(f) {
+ Some(ff) => fixmes.contains(ff) || !ff.exists(),
+ None => true,
+ })
+ .cloned()
+ .collect::<BTreeSet<_>>();
+ Ok(fixmes)
+}
+
+pub fn rel_p<'a>(path: &'a Path, base: &'a Path) -> &'a str {
+ path.strip_prefix(base).unwrap_or(path).to_str().unwrap()
+}
+
+pub fn copy_tree(oldtree: &Path, newtree: &Path) -> Result<(), Error> {
+ for entry in WalkDir::new(oldtree) {
+ let entry = entry?;
+ if entry.depth() == 0 {
+ continue;
+ }
+ let oldpath = entry.path();
+ let newpath = newtree.join(oldpath.strip_prefix(&oldtree).unwrap());
+ let ftype = entry.file_type();
+ if ftype.is_dir() {
+ fs::create_dir(newpath)?;
+ } else if ftype.is_file() {
+ fs::copy(oldpath, newpath)?;
+ } else if ftype.is_symlink() {
+ symlink(fs::read_link(oldpath)?, newpath)?;
+ }
+ }
+ Ok(())
+}
+
+pub fn show_vec_with<'a, T, F>(it: impl IntoIterator<Item = &'a T>, f: F) -> String
+where
+ T: 'a,
+ F: FnMut(&T) -> String,
+{
+ Itertools::intersperse(it.into_iter().map(f), ", ".to_string()).collect::<String>()
+}
+
+pub fn show_vec<'a, T>(it: impl IntoIterator<Item = &'a T>) -> String
+where
+ T: fmt::Display + 'a,
+{
+ show_vec_with(it, std::string::ToString::to_string)
+}
+
+pub fn expect_success(cmd: &mut Command, err: &str) {
+ match cmd.status() {
+ Ok(status) => {
+ if !status.success() {
+ panic!("{}", err);
+ }
+ }
+ Err(e) => {
+ panic!("{}\n{}", err, e);
+ }
+ }
+}
+
+pub(crate) fn traverse_depth<'a, V, F>(succ: &'a F, key: V) -> BTreeSet<V>
+where
+ V: Ord + Copy + 'a,
+ F: Fn(&V) -> Option<&'a Vec<V>>,
+{
+ let mut remain = VecDeque::from_iter([key]);
+ let mut seen = BTreeSet::new();
+ while let Some(v) = remain.pop_front() {
+ for v_ in succ(&v).into_iter().flatten() {
+ if !seen.contains(v_) {
+ seen.insert(*v_);
+ remain.push_back(*v_);
+ }
+ }
+ }
+ seen
+}
+
+/// Get a value that might be set at a key or any of its ancestor keys,
+/// whichever is closest. Error if there are conflicting definitions.
+#[allow(clippy::type_complexity)]
+pub(crate) fn get_transitive_val<
+ 'a,
+ P: Fn(K) -> Option<&'a Vec<K>>,
+ F: Fn(K) -> Option<V>,
+ K: 'a + Ord + Copy,
+ V: Eq + Ord,
+>(
+ getparents: &'a P,
+ f: &F,
+ key: K,
+) -> Result<Option<V>, (K, Vec<(K, V)>)> {
+ let here = f(key);
+ if here.is_some() {
+ // value overrides anything from parents
+ Ok(here)
+ } else {
+ let mut candidates = Vec::new();
+ for par in getparents(key).into_iter().flatten() {
+ if let Some(v) = get_transitive_val(getparents, f, *par)? {
+ candidates.push((*par, v))
+ }
+ }
+ if candidates.is_empty() {
+ Ok(None) // here is None
+ } else {
+ let mut values = candidates.iter().map(|(_, v)| v).collect::<Vec<_>>();
+ values.sort();
+ values.dedup();
+ if values.len() == 1 {
+ Ok(candidates.pop().map(|(_, v)| v))
+ } else {
+ Err((key, candidates)) // handle conflict
+ }
+ }
+ }
+}
+
+pub fn graph_from_succ<V, FV, FL, E>(
+ seed: impl IntoIterator<Item = V>,
+ succ: &mut FV,
+ log: &mut FL,
+) -> Result<BTreeMap<V, BTreeSet<V>>, E>
+where
+ V: Ord + Clone,
+ FV: FnMut(&V) -> Result<(Vec<V>, Vec<V>), E>,
+ FL: FnMut(&VecDeque<V>, &BTreeMap<V, BTreeSet<V>>) -> Result<(), E>,
+{
+ let mut seen = BTreeSet::from_iter(seed);
+ let mut graph = BTreeMap::new();
+ let mut remain = VecDeque::from_iter(seen.iter().cloned());
+ while let Some(v) = remain.pop_front() {
+ log(&remain, &graph)?;
+ let (hard, soft) = succ(&v)?;
+ for v_ in hard.iter().chain(soft.iter()) {
+ if !seen.contains(v_) {
+ seen.insert(v_.clone());
+ remain.push_back(v_.clone());
+ }
+ }
+ graph.insert(v, BTreeSet::from_iter(hard));
+ }
+ Ok(graph)
+}
+
+pub fn succ_proj<S, T, F>(succ: &BTreeMap<S, BTreeSet<S>>, proj: F) -> BTreeMap<T, BTreeSet<T>>
+where
+ F: Fn(&S) -> T,
+ S: Ord,
+ T: Ord + Clone,
+{
+ let mut succ_proj: BTreeMap<T, BTreeSet<T>> = BTreeMap::new();
+ for (s, ss) in succ {
+ let e = succ_proj.entry(proj(s)).or_default();
+ for s_ in ss {
+ e.insert(proj(s_));
+ }
+ }
+ succ_proj
+}
+
+pub fn succ_to_pred<V>(succ: &BTreeMap<V, BTreeSet<V>>) -> BTreeMap<V, BTreeSet<V>>
+where
+ V: Ord + Clone,
+{
+ let mut pred: BTreeMap<V, BTreeSet<V>> = BTreeMap::new();
+ for (v, vv) in succ {
+ for v_ in vv {
+ pred.entry(v_.clone()).or_default().insert(v.clone());
+ }
+ }
+ pred
+}
+
+pub fn topo_sort<V>(
+ seed: impl IntoIterator<Item = V>,
+ succ: BTreeMap<V, BTreeSet<V>>,
+ mut pred: BTreeMap<V, BTreeSet<V>>,
+) -> Result<Vec<V>, BTreeMap<V, BTreeSet<V>>>
+where
+ V: Ord + Clone,
+{
+ let empty = BTreeSet::new();
+ let mut remain = VecDeque::from_iter(seed);
+ let mut sort = Vec::new();
+ while let Some(v) = remain.pop_front() {
+ sort.push(v.clone());
+ for v_ in succ.get(&v).unwrap_or(&empty) {
+ let par = pred.entry(v_.clone()).or_default();
+ par.remove(&v);
+ if par.is_empty() {
+ remain.push_back(v_.clone());
+ }
+ }
+ }
+ pred.retain(|_, v| !v.is_empty());
+ if !pred.is_empty() {
+ Err(pred)
+ } else {
+ Ok(sort)
+ }
+}
--- /dev/null
+uploaders = ["Sylvestre Ledru <sylvestre@debian.org>", "Ximin Luo <infinity0@debian.org>" ]
+
+[source]
+policy = "4.0.0"
+homepage = "https://clap.rs"
+
+[packages.lib]
+summary = "Simple, efficient and full featured Command line argument parser - source"
+description = """
+clap is used to parse and validate string of command line arguments provided by
+user at runtime. It provides help and version flags by default and additionally
+provide help subcommands in addition to traditional flags.
+This package provides clap with following default features.
+ * suggestions: provides did you mean suggestions on typos
+ * color: turns on colored error messages.
+ * wrap_help: Wrap the help at actual terminal width when available.
+"""
--- /dev/null
+extern crate debcargo;
+
+use debcargo::config::{Config, PackageKey};
+use std::path::Path;
+
+#[test]
+fn source_package_override() {
+ let filepath = Path::new("tests/clap_override.toml");
+
+ let config = Config::parse(filepath);
+ assert!(config.is_ok());
+
+ let config = config.unwrap();
+
+ assert!(config.source.is_some());
+ assert!(config.packages.is_some());
+
+ let policy = config.policy_version();
+ assert!(policy.is_some());
+ assert_eq!(policy.unwrap(), "4.0.0");
+
+ let homepage = config.homepage();
+ assert!(homepage.is_some());
+ assert_eq!(homepage.unwrap(), "https://clap.rs");
+
+ assert!(config.section().is_none());
+ assert!(config.build_depends().is_none());
+
+ let filepath = Path::new("tests/debcargo_override.toml");
+ let config = Config::parse(filepath);
+ assert!(config.is_ok());
+
+ let config = config.unwrap();
+
+ assert!(config.source.is_some());
+
+ let section = config.section();
+ assert!(section.is_some());
+ assert_eq!(section.unwrap(), "rust");
+
+ assert!(config.packages.is_some());
+ let sd = config.package_summary(PackageKey::Bin);
+ assert!(sd.is_some());
+
+ if let Some(s) = sd {
+ assert_eq!(s, "Tool to create Debian package from Rust crate");
+ }
+
+ let sd = config.package_description(PackageKey::Bin);
+ assert!(sd.is_some());
+ if let Some(d) = sd {
+ assert_eq!(
+ d,
+ "\
+This package provides debcargo a tool to create Debian source package from \
+ Rust
+crate. The package created by this tool is as per the packaging policy \
+ set by
+Debian Rust team.
+"
+ );
+ }
+}
+
+#[test]
+fn sd_top_level() {
+ let filepath = Path::new("tests/debcargo_override_top_level.toml");
+ let config = Config::parse(filepath);
+ assert!(config.is_ok());
+
+ let config = config.unwrap();
+
+ assert!(config.source.is_some());
+
+ let section = config.section();
+ assert!(section.is_some());
+ assert_eq!(section.unwrap(), "rust");
+
+ assert_eq!(
+ config.summary.unwrap(),
+ "Tool to create Debian package from Rust crate"
+ );
+ assert_eq!(
+ config.description.unwrap(),
+ "\
+This package provides debcargo a tool to create Debian source package from \
+ Rust
+crate. The package created by this tool is as per the packaging policy \
+ set by
+Debian Rust team.
+"
+ );
+}
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate requires builder to set custom RUSTFLAGS
+# we can't do that in Debian due to architecture baselines
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate requires builder to set custom RUSTFLAGS
+# we can't do that in Debian due to architecture baselines
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
+
+[source]
+build_depends = ["llvm", "lld", "clang"]
+
+[packages.lib]
+depends = ["libclang-dev", "llvm", "lld", "clang"]
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+# needed by build script
+export XDG_DATA_HOME := $(CURDIR)/debian/xdg-data-home
+export CXXFLAGS := -Wno-unused-command-line-argument
+export CFLAGS := -Wno-unused-command-line-argument
+
+override_dh_strip_nondeterminism:
+ # dh_strip_nondeterminism can't parse these files
+ rm -rf $(CURDIR)/debian/libruzt-afl-*-dev/usr/share/cargo/registry/afl-*/AFLplusplus/docs/vuln_samples
+ dh_strip_nondeterminism
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["libaom-dev"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Crate authors like to put dev-dependencies in dependencies due to
+https://github.com/rust-lang/cargo/issues/1596
+
+Then due to lack of maintenance this pollutes the dependency set with old
+crates that no longer compile.
+
+Here, quickcheck 0.4 eventually depends on simd which no longer compiles.
+
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -22,9 +22,6 @@
+ [[test]]
+ name = "tests"
+ path = "tests.rs"
+-[dependencies.quickcheck]
+-version = "0.4.1"
+-optional = true
+
+ [features]
+ default = ["std"]
--- /dev/null
+fix-old-dep.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_strip:
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_dwz:
+ # Don't do anything. fails because of the
+ # https://github.com/rust-lang/rust/issues/66118
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_dwz:
+ # Don't do anything. fails because of the
+ # https://github.com/rust-lang/rust/issues/66118
--- /dev/null
+semver_suffix = true
+overlay = "."
+
+[source]
+build_depends = ["llvm", "clang"]
+
+[packages.lib]
+depends = ["libclang-dev", "lld", "clang"]
--- /dev/null
+semver_suffix = true
+overlay = "."
+
+[source]
+build_depends = ["llvm", "clang"]
+
+[packages.lib]
+depends = ["libclang-dev", "lld", "clang"]
--- /dev/null
+semver_suffix = true
+overlay = "."
+
+[source]
+build_depends = ["llvm", "clang"]
+
+[packages.lib]
+depends = ["libclang-dev", "lld", "clang"]
--- /dev/null
+semver_suffix = true
+overlay = "."
+
+[source]
+build_depends = ["llvm", "clang"]
+
+[packages.lib]
+depends = ["libclang-dev", "lld", "clang"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# clippy upstream now errors on purpose with the message "clippy is no longer
+# available on crates.io". Many old crates depend on clippy even though it is
+# a dev-dependency because of [3] and there are far too many to patch. So just
+# make the build of clippy "succeed"; it does not actually get used in the
+# build of a reverse-dependency anyway.
+#
+# [1] https://github.com/rust-lang/cargo/issues/1596
+
+override_dh_auto_test:
+ dh_auto_test || true
+
+override_dh_auto_install:
+ dh_auto_install || true
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["cmake"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate errors on the default feature by design
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+bin = false
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["libdav1d-dev"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Crate authors like to put dev-dependencies in dependencies due to
+https://github.com/rust-lang/cargo/issues/1596
+
+Then due to lack of maintenance this pollutes the dependency set with old
+crates that no longer compile.
+
+Here, quickcheck 0.4 eventually depends on simd which no longer compiles.
+
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -76,10 +76,6 @@
+ version = ">=0.3.0, <0.5.0"
+ optional = true
+
+-[dependencies.quickcheck]
+-version = "0.4"
+-optional = true
+-
+ [dependencies.r2d2]
+ version = ">= 0.8, < 0.9"
+ optional = true
--- /dev/null
+fix-old-dep.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on windows
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_dwz:
+ # Don't do anything. fails because of the
+ # https://github.com/rust-lang/rust/issues/66118
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -83,7 +83,7 @@
+ version = "0.2.0"
+
+ [features]
+-async_std = ["fs"]
++async_std = ["async-std"] # replaces fs with deps of fs
+ cookie-secure = ["cookies", "cookie/secure"]
+ cookies = ["cookie"]
+ default = ["fs", "cookie-secure"]
--- /dev/null
+fix-features.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Index: hyperx-1.4.0/Cargo.toml
+===================================================================
+--- hyperx-1.4.0.orig/Cargo.toml
++++ hyperx-1.4.0/Cargo.toml
+@@ -43,7 +43,7 @@ version = ">=0.3.1, <0.4"
+ version = ">=0.3.2, <0.4"
+
+ [dependencies.percent-encoding]
+-version = ">=2.1.0, <2.2"
++version = "2.1.0"
+
+ [dependencies.unicase]
+ version = ">=2.6.0, <2.7"
--- /dev/null
+relax-percent-encoding.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Index: hyperx-1.4.0/Cargo.toml
+===================================================================
+--- hyperx-1.4.0.orig/Cargo.toml
++++ hyperx-1.4.0/Cargo.toml
+@@ -43,7 +43,7 @@ version = ">=0.3.1, <0.4"
+ version = ">=0.3.2, <0.4"
+
+ [dependencies.percent-encoding]
+-version = ">=2.1.0, <2.2"
++version = "2.1.0"
+
+ [dependencies.unicase]
+ version = ">=2.6.0, <2.7"
--- /dev/null
+relax-percent-encoding.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["nasm"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate declares benches outside of crate path
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Index: once_cell-1.16.0/Cargo.toml
+===================================================================
+--- once_cell-1.16.0.orig/Cargo.toml
++++ once_cell-1.16.0/Cargo.toml
+@@ -66,7 +66,7 @@ required-features = ["std"]
+ name = "test_synchronization"
+ required-features = ["std"]
+
+-[dependencies.atomic_polyfill]
++[dependencies.atomic_polyfill_renamed]
+ version = "1"
+ optional = true
+ package = "atomic-polyfill"
+@@ -100,7 +100,7 @@ alloc = ["race"]
+ atomic-polyfill = ["critical-section"]
+ critical-section = [
+ "critical_section",
+- "atomic_polyfill",
++ "atomic_polyfill_renamed",
+ ]
+ default = ["std"]
+ parking_lot = ["parking_lot_core"]
--- /dev/null
+fix-feature.patch
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on windows
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["libssl-dev"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+Crate authors like to put dev-dependencies in dependencies due to
+https://github.com/rust-lang/cargo/issues/1596
+
+Then due to lack of maintenance this pollutes the dependency set with old
+crates that no longer compile.
+
+Here, quickcheck 0.4 eventually depends on simd which no longer compiles.
+
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -21,7 +21,7 @@
+ license = "MIT/Apache-2.0"
+ repository = "https://github.com/bluss/petgraph"
+ [package.metadata.docs.rs]
+-features = ["serde-1", "quickcheck"]
++features = ["serde-1"]
+
+ [package.metadata.release]
+ no-dev-version = true
+@@ -40,11 +40,6 @@
+ version = "0.3.0"
+ optional = true
+
+-[dependencies.quickcheck]
+-version = "0.4"
+-optional = true
+-default-features = false
+-
+ [dependencies.serde]
+ version = "1.0"
+ optional = true
+@@ -66,7 +61,7 @@
+ version = "0.3"
+
+ [features]
+-all = ["unstable", "quickcheck", "stable_graph", "graphmap"]
++all = ["unstable", "stable_graph", "graphmap"]
+ default = ["graphmap", "stable_graph"]
+ generate = []
+ graphmap = ["ordermap"]
--- /dev/null
+fix-old-dep.patch
--- /dev/null
+semver_suffix = true
+bin = false
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["pkg-config"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate is buggy; fails to declare "alloc" feature on futures-util
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_dwz:
+ # Don't do anything. fails because of the
+ # https://github.com/rust-lang/rust/issues/66118
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate is super-over-zealous with lint settings
+# overrides our cargo wrapper's --cap-lints warn
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate is super-over-zealous with lint settings
+# overrides our cargo wrapper's --cap-lints warn
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on macos
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-apple-darwin) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+actix-web eventually depends on actix-connect 1.0.2
+actix-connect 1.0.2 depends on prerelease trust-dns-resolver 0.18.0-alpha.2,
+which declares broken deps on trust-dns-proto "0.18.0" - should be "=0.18.0-alpha.2"
+
+debcargo doesn't support prerelease versions because semver compatibility
+doesn't apply. So patching trust-dns-resolver to fix their bug is not an
+option for us here. So just disable actix-web 2 here.
+
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -30,12 +30,6 @@
+ default-features = false
+ package = "actix-web"
+
+-[dependencies.actix-web2]
+-version = "2.0"
+-optional = true
+-default-features = false
+-package = "actix-web"
+-
+ [dependencies.futures]
+ version = "0.3"
+ optional = true
+@@ -75,6 +69,5 @@
+
+ [features]
+ actix = ["actix-web", "futures"]
+-actix2 = ["actix-web2", "futures"]
+ default = []
+ warp = ["futures", "tracing", "warp-framework"]
--- /dev/null
+fix-actix-web-dep.patch
--- /dev/null
+semver_suffix = true
+
+[packages.lib]
+depends = ["libfontconfig-dev"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# code uses old rust feature that causes compiler error.
+override_dh_auto_test:
+ dh_auto_test || true
+
+# This crate is actually broken; however there are several cases why this
+# override is OK and necessary for Debian:
+#
+# 1. The other crate depends on this broken crate in an architecture-specific
+# way i.e. with [target.$arch.*] in Cargo.toml
+#
+# cargo does not yet support omitting these dependencies on other arches [1]
+# and so we are forced to include it also in debcargo [2]
+#
+# [1] https://github.com/rust-lang/cargo/issues/5896
+# [2] https://salsa.debian.org/rust-team/debcargo/-/issues/14
+#
+# 2. The other crate depends on this broken crate only via one of its optional
+# features that most users of that crate, don't need. You generally run into
+# this situation when resolving via --resolve-type BinaryAllForDebianTesting
+#
+# If your situation does not fit into one of the above situations, you should
+# re-consider the override. If your override is incorrect, the build failure
+# will crop up again later, in the crate that depends on this broken crate.
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# crate errors on purpose when no features are enabled
+override_dh_auto_test:
+ dh_auto_test || true
--- /dev/null
+semver_suffix = true
+excludes = ["test-data/**"]
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on windows
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+%:
+ dh $@ --buildsystem cargo
+export RUSTC_BOOTSTRAP := 1
+export PATH := $(CURDIR)/debian/debcargo_testing_bin:$(PATH)
+
+override_dh_strip:
--- /dev/null
+semver_suffix = true
+overlay = "."
--- /dev/null
+#!/usr/bin/make -f
+include /usr/share/rustc/architecture.mk
+%:
+ dh $@ --buildsystem cargo
+
+# only supported on windows
+override_dh_auto_test:
+ case $(DEB_HOST_RUST_TYPE) in *-pc-windows-gnu) dh_auto_test;; *) true;; esac
--- /dev/null
+semver_suffix = true
+# tests purposefully contain invalid data that debian tools try to process
+excludes = ["tests/**"]
--- /dev/null
+uploaders = ["Sylvestre Ledru <sylvestre@debian.org>", "Ximin Luo <infinity0@debian.org>" ]
+
+[source]
+section = "rust"
+build_depends = ["libssl-dev"]
+homepage = "https://salsa.debian.org/rust-team/debcargo-conf"
+
+[packages.bin]
+summary = "Tool to create Debian package from Rust crate"
+description = """
+This package provides debcargo a tool to create Debian source package from Rust
+crate. The package created by this tool is as per the packaging policy set by
+Debian Rust team.
+"""
--- /dev/null
+uploaders = ["Sylvestre Ledru <sylvestre@debian.org>", "Ximin Luo <infinity0@debian.org>" ]
+
+summary = "Tool to create Debian package from Rust crate"
+description = """
+This package provides debcargo a tool to create Debian source package from Rust
+crate. The package created by this tool is as per the packaging policy set by
+Debian Rust team.
+"""
+
+[source]
+section = "rust"
+build_depends = ["libssl-dev"]
+homepage = "https://salsa.debian.org/rust-team/debcargo-conf"
--- /dev/null
+#!/bin/bash
+set -e
+
+scriptdir="$(dirname "$0")"
+
+# outputs
+directory=tmp
+failures_file=""
+# inputs
+allow_failures="$scriptdir/build-allow-fail"
+lintian_suppress_tags="$scriptdir/lintian-suppress-tags"
+config_dir="$scriptdir/../configs"
+# tweaks
+run_lintian=true
+run_sbuild=false
+keepfiles=false
+resolve=
+extraargs=
+
+export DEBCARGO_TESTING_IGNORE_DEBIAN_POLICY_VIOLATION=1
+export DEBCARGO_TESTING_RUZT=1
+
+export DEB_HOST_ARCH=${DEB_HOST_ARCH:-$(dpkg-architecture -qDEB_HOST_ARCH)}
+
+while getopts 'd:f:a:l:c:bkrRux:zh?' o; do
+ case $o in
+ d ) directory=$OPTARG;;
+ f ) failures_file=$OPTARG;;
+
+ a ) allow_failures=$OPTARG;;
+ c ) config_dir=$OPTARG;;
+
+ b ) run_sbuild=true;;
+ k ) keepfiles=true;;
+ r ) resolve=SourceForDebianUnstable;;
+ R ) resolve=BinaryAllForDebianTesting;;
+ x ) extraargs="$extraargs $OPTARG";;
+ h|\? ) cat >&2 <<eof
+Usage: $0 [-ru] (<crate name>|<path/to/crate>) [..]
+
+Run debcargo, do a source-only build, and call lintian on the results.
+
+ -h This help text.
+
+Options for output:
+ -d DIR Output directory, default: $directory. Warning: this will be
+ wiped at the start of the test!
+ -f FILE File to output failed crates in, instead of exiting non-zero.
+ Relative paths are taken relative to the output directory.
+
+Options for input:
+ -a FILE File that lists crate names to ignore failures for, default:
+ $allow_failures.
+ -c DIR Path to config directory, default: $config_dir.
+
+Options to control running:
+ -b Run sbuild on the resulting dsc package.
+ -k Don't wipe the output directory at the start of the test, and
+ don't rebuild a crate if its directory already exists.
+ -r Operate on all transitive build-dependencies of the source
+ package, needed for entry into Debian Unstable.
+ -R Operate on all transitive dependencies of the binary packages,
+ needed for entry into Debian Testing.
+ -x ARG Give ARG as an extra argument to debcargo, e.g. like
+ -x--copyright-guess-harder.
+eof
+ exit 2;;
+ esac
+done
+shift $(expr $OPTIND - 1)
+
+allow_fail() {
+ local crate="$1"
+ local version="$2"
+ if ! test -f "${allow_failures}"; then
+ return 1
+ elif grep -qx "${crate}" "${allow_failures}"; then
+ echo >&2 "Allowing ${crate} to fail..."
+ return 0
+ elif [ -n "$version" ] && grep -qx "${crate}-${version}" "${allow_failures}"; then
+ echo >&2 "Allowing ${crate}-${version} to fail..."
+ return 0
+ else
+ return 1
+ fi
+}
+
+shouldbuild() {
+ local dst="$1"
+ local src="$2"
+ test ! -e "$dst" -o "$src" -nt "$dst"
+}
+
+changelog_pkgname() {(
+ local cratedir="$1"
+ cd "$cratedir"
+ # dpkg-parsechangelog is really slow when dealing with hundreds of crates
+ #echo $(dpkg-parsechangelog -SSource)_$(dpkg-parsechangelog -SVersion)
+ head -n1 debian/changelog | sed -nre 's/^(\S*) \((\S*)\).*/\1_\2/gp'
+)}
+
+run_lintian() {(
+ local crate="$1"
+ local version="$2"
+ local cratedir="$crate${version:+-$version}"
+ cd "$directory"
+
+ allow_fail "$crate" $version && return 0
+
+ local base="$(changelog_pkgname "$cratedir")"
+ local out="${base}.lintian.out"
+
+ if ! ( shouldbuild "$out" "${base}_source.changes" \
+ || shouldbuild "$out" "${base}_${DEB_HOST_ARCH}.changes" ); then
+ echo >&2 "skipping already-linted ${base}_*.changes in ${out}"
+ return 0
+ fi
+
+ echo >&2 "running lintian for ${base} into ${out}"
+ rm -f "$out" "${out}.tmp"
+ changes="${base}_source.changes"
+ lintian --suppress-tags-from-file "$lintian_suppress_tags" -EIL +pedantic "$changes" | tee -a "${out}.tmp"
+ changes="${base}_${DEB_HOST_ARCH}.changes"
+ lintian --suppress-tags-from-file "$lintian_suppress_tags" -EIL +pedantic "$changes" | tee -a "${out}.tmp"
+ mv "${out}.tmp" "$out"
+)}
+
+if [ -z "$CHROOT" ]; then
+ if schroot -i -c "debcargo-unstable-${DEB_HOST_ARCH}-sbuild" >/dev/null 2>&1; then
+ CHROOT="debcargo-unstable-${DEB_HOST_ARCH}-sbuild"
+ else
+ CHROOT=${CHROOT:-unstable-"$DEB_HOST_ARCH"-sbuild}
+ fi
+fi
+GPG_KEY_ID="Debcargo Integration Test"
+run_sbuild() {(
+ local crate="$1"
+ local version="$2"
+ local cratedir="$crate${version:+-$version}"
+ cd "$directory"
+
+ allow_fail "$crate" $version && return 0
+ local base="$(changelog_pkgname "$cratedir")"
+ local dsc="${base}.dsc"
+ local build="${base}_${DEB_HOST_ARCH}.build"
+ local changes="${base}_${DEB_HOST_ARCH}.changes"
+
+ if ! shouldbuild "$changes" "$dsc"; then
+ echo >&2 "skipping already-built ${dsc} in ${changes}"
+ return 0
+ fi
+
+ if [ ! -f "signing-key.gpg" ]; then
+ mkdir -p "$PWD/gpg"
+ chmod 700 "$PWD/gpg"
+ GNUPGHOME="$PWD/gpg" gpg --batch --pinentry-mode=loopback --passphrase "" --quick-gen-key "$GPG_KEY_ID"
+ GNUPGHOME="$PWD/gpg" gpg --batch --export "$GPG_KEY_ID" > signing-key.gpg
+ fi
+
+ # Update the local repo
+ apt-ftparchive packages . > Packages
+ apt-ftparchive release . > Release
+ GNUPGHOME="$PWD/gpg" gpg --batch -a --detach-sign -u "$GPG_KEY_ID" -o Release.gpg --yes Release
+ # We use --build-dep-resolver=aspcud as both apt/aptitude fail to resolve
+ # certain complex dependency situations e.g. bytes-0.4. For our official
+ # Debian rust packages we patch those crates to have simpler dependencies;
+ # but we don't want to maintain those patches for this integration test.
+ # We also pass criteria to minimise the Rust packages we take from the
+ # Debian archive, and maximise the ones generated by this test.
+ echo >&2 "sbuild $dsc logging to $build"
+ sbuild --arch-all --arch-any --no-run-lintian --build-dep-resolver=aspcud \
+ --aspcud-criteria="-removed,-changed,-new,+count(solution,APT-Release:=/o=sbuild-build-depends-archive/),-count(solution,APT-Release:=/o=Debian/)" \
+ --extra-repository="deb file:$(readlink -f "$directory") ./" --extra-repository-key="$PWD/signing-key.gpg" \
+ -c "$CHROOT" -d unstable $SBUILD_EXTRA_ARGS "$dsc"
+)}
+
+build_source() {(
+ local crate="$1"
+ local version="$2"
+ local cratedir="$crate${version:+-$version}"
+ cd "$directory"
+
+ if [ -d "$cratedir" ]; then
+ if [ -f "$cratedir/debian/changelog" ]; then
+ local base="$(changelog_pkgname "$cratedir")"
+ if ! shouldbuild "${base}_source.buildinfo" "$cratedir/debian/changelog"; then
+ echo >&2 "skipping already-built ${cratedir}"
+ return 0
+ fi
+ fi
+ rm -rf "$cratedir"
+ fi
+
+ local deb_src_name="$($debcargo deb-src-name "$crate" "$version")"
+ local config="$config_dir/${deb_src_name}/debian/debcargo.toml"
+ if [ -f "$config" ]; then
+ option="--config $config"
+ if ! grep -q 'semver_suffix = true' "$config"; then
+ echo >&2 "bad config: $config must contain \"semver_suffix = true\""
+ return 1
+ fi
+ echo >&2 "using config: $config"
+ elif [ "$deb_src_name" != "$($debcargo deb-src-name "$crate" "")" ]; then
+ config="$config_dir/old-version/debian/debcargo.toml"
+ option="--config $config"
+ echo >&2 "using config: $config"
+ fi
+
+ if ( set -x; $debcargo package $extraargs --no-overlay-write-back --directory $cratedir $option "${crate}" $version ); then
+ :
+ else
+ local x=$?
+ if allow_fail "$crate" $version; then
+ return 0
+ fi
+ echo >&2 "crate failed: $crate $version"
+ if [ -n "$failures_file" ]; then
+ echo "$crate" $version >> "$failures_file"
+ return 0
+ else
+ return $x
+ fi
+ fi
+ cd "${cratedir}"
+ mkdir -p debian/source
+ dpkg-buildpackage -d -S --no-sign
+)}
+
+cargo_tree_rec() {
+ local resolve="$1"
+ shift
+ local cache="$directory/z.${*/\//_}.$resolve.list"
+ if [ ! -f "$cache" ]; then
+ "$debcargo" build-order --resolve-type "$resolve" \
+ --config-dir "${config_dir}" "$@" > "$cache.tmp"
+ mv "$cache.tmp" "$cache"
+ fi
+ cat "$cache"
+}
+
+run_x_or_deps() {
+ local x="$1"
+ shift
+ case "$x" in
+ *-[0-9]*)
+ spec="${x%-[0-9]*} ${x##*-}"
+ tree_args="${x%-[0-9]*}:${x##*-}"
+ ;;
+ *)
+ spec="$x"
+ tree_args="$x"
+ ;;
+ esac
+ if [ -n "$resolve" ]; then
+ set -o pipefail
+ cargo_tree_rec "$resolve" $tree_args | while read pkg ver extra; do
+ "$@" "$pkg" "${ver#v}"
+ done
+ set +o pipefail
+ fi
+ echo $spec | while read pkg ver extras; do
+ "$@" "$pkg" "${ver#v}"
+ done
+}
+
+# make all paths absolute so things don't mess up when we switch dirs
+allow_failures=$(readlink -f "$allow_failures")
+lintian_suppress_tags=$(readlink -f "$lintian_suppress_tags")
+config_dir=$(readlink -f "$config_dir")
+directory=$(readlink -f "$directory")
+scriptdir=$(readlink -f "$scriptdir")
+
+# ensure $directory exists and maybe wipe it
+if ! $keepfiles; then
+ # don't rm the directory itself, in case it's a symlink
+ rm -rf "$directory"/*
+fi
+mkdir -p "$directory"
+
+cargo build
+debcargo="$scriptdir/../../target/debug/debcargo"
+test -x $debcargo
+
+for i in "$@"; do run_x_or_deps "$i" true; done
+for i in "$@"; do run_x_or_deps "$i" build_source; done
+# sudo schroot -c source:debcargo-unstable-amd64-sbuild -- sh -c 'echo "deb [allow-insecure=yes] file:/home/infinity0/var/lib/rust/debcargo-tmp ./" > /etc/apt/sources.list.d/local-debcargo-integration-test.list'
+if $run_sbuild; then
+ if ! schroot -i -c "$CHROOT" >/dev/null; then
+ echo >&2 "create the $CHROOT schroot by running e.g.:"
+ echo >&2 " sudo sbuild-createchroot unstable /srv/chroot/$CHROOT http://deb.debian.org/debian"
+ echo >&2 " sudo schroot -c source:$CHROOT -- apt-get -y install dh-cargo"
+ echo >&2 " sudo sbuild-update -udr $CHROOT"
+ echo >&2 "See https://wiki.debian.org/sbuild for more details"
+ exit 1
+ fi
+ for i in "$@"; do run_x_or_deps "$i" run_sbuild; done
+fi
+if $run_lintian; then
+ for i in "$@"; do run_x_or_deps "$i" run_lintian; done
+fi
--- /dev/null
+## To be fixed by the package developer
+
+# copyright
+missing-license-paragraph-in-dep5-copyright
+space-in-std-shortname-in-dep5-copyright
+license-problem-undefined-license
+superfluous-file-pattern
+bad-exception-format-in-dep5-copyright
+
+# other source
+file-contains-fixme-placeholder
+synopsis-too-long
+description-synopsis-starts-with-article
+debian-changelog-line-too-long
+unknown-section
+spelling-error-in-binary
+rust-boilerplate
+no-manual-page
+
+## To be fixed here in debcargo
+
+out-of-date-standards-version
+package-uses-old-debhelper-compat-version
+uses-debhelper-compat-file
+upstream-metadata-file-is-missing
+
+## Cannot be fixed due to Rust crate conventions
+
+# crates don't generally have GPG signatures
+debian-watch-does-not-check-gpg-signature
+# crates don't always have a homepage
+no-homepage-field
+# crates don't always have tests
+missing-tests-control
+# crates sometimes just have long source lines
+very-long-line-length-in-source-file
+
+## Not to be fixed, due to how we package Rust in Debian and lintian being presumptuous
+
+# we install the crate into /usr/share/cargo, including most docs
+package-contains-documentation-outside-usr-share-doc
+# we install the crate into /usr/share/cargo, lintian is overzealous
+repeated-path-segment
+
+# this is a result of how we have to do rust packaging -
+# see the comment relating to Multi-Arch in the debcargo source code
+package-contains-no-arch-dependent-files
+
+# Provides issue with FTP pending resolution
+field-too-long
+
+# bug 833608
+version-substvar-for-external-package
+
+# our policy is not to file ITPs for library crates since there are so many
+initial-upload-closes-no-bugs
+
+## Not to be fixed, due to our test setup
+
+unused-override
+# lintian doesn't recognise our test "ruzt" prefix
+wrong-section-according-to-package-name
+
+# we don't finalise changelogs for this integration test
+bad-distribution-in-changes-file
+distribution-and-changes-mismatch
+
+# this is because we don't bother with excluding embedded libs in this
+# integration test. they are not allowed in Debian and would be a FTP reject
+# but we don't care for the purposes of this integration test
+
+source-is-missing
+unpack-message-for-orig
+unpack-message-for-deb-data
+unpack-message-for-source
+arch-dependent-file-in-usr-share
+arch-dependent-file-not-in-arch-specific-directory
+arch-dep-package-has-big-usr-share
+embedded-library
+font-outside-font-dir
+font-in-non-font-package
+duplicate-font-file
+source-contains-autogenerated-gperf-data
+source-contains-prebuilt-javascript-object
+windows-devel-file-in-package
+executable-not-elf-or-script
+incorrect-path-for-interpreter
+hardening-no-fortify-functions
--- /dev/null
+#!/bin/bash
+set -e
+cargo build
+rm -rf rust-*
+target/debug/debcargo package "$@"
+nano rust-${1/_/-}-*/debian/${file:-control}